Skip to content

Commit 589694b

Browse files
committed
Restore IP_SOCK_getsockopt emNET error lookup
Merging TranslateReturnCode into wolfSSL_LastError dropped the IP_SOCK_getsockopt(SO_ERROR) lookup emNET integrations need to retrieve the canonical IP_ERR_* for a failed recv/send, leaving a broken branch that returned the raw value and mishandled the POSIX-facade convention. Restore the historic lookup (fixing the optlen pointer-vs-int typo along the way) and add a CI test that builds wolfSSL with -DWOLFSSL_EMNET against a clean-room shim providing an emNET-faithful IP_SOCK_getsockopt (SO_ERROR-then-errno fallback, since Linux does not stash EAGAIN in SO_ERROR); recv/send fall through to glibc.
1 parent 91463b9 commit 589694b

7 files changed

Lines changed: 133 additions & 102 deletions

File tree

.github/workflows/emnet-nonblock.yml

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,10 @@ concurrency:
1414

1515
# Build wolfSSL with -DWOLFSSL_EMNET using the clean-room shim in
1616
# tests/emnet/ (IP/IP.h + emnet_shim.c), link a non-blocking TLS 1.3
17-
# handshake test with -Wl,--wrap=recv,--wrap=send, and run it. The
18-
# test exercises the WOLFSSL_EMNET path in wolfSSL_LastError() and
19-
# asserts that would-block events surface as WANT_READ/WANT_WRITE and
20-
# the handshake completes, guarding against regressions in the emNET
21-
# error-translation logic.
17+
# handshake test, and run it. The test exercises the WOLFSSL_EMNET
18+
# path in wolfSSL_LastError() and asserts that would-block events
19+
# surface as WANT_READ/WANT_WRITE and the handshake completes,
20+
# guarding against regressions in the emNET error-translation logic.
2221

2322
jobs:
2423
emnet_nonblock:
@@ -44,10 +43,16 @@ jobs:
4443
--enable-static --disable-shared \
4544
--enable-tls13 --disable-oldtls \
4645
--enable-ecc --disable-examples \
46+
--disable-benchmark --disable-crypttests \
4747
CFLAGS="-DWOLFSSL_EMNET -I$(pwd)/tests/emnet"
4848
49-
- name: Build wolfSSL
50-
run: make -j$(nproc)
49+
# The WOLFSSL_EMNET branch in src/wolfio.c calls IP_SOCK_getsockopt
50+
# which only resolves against the test shim. Build only the static
51+
# library; examples/benchmark/crypttests are disabled above, but
52+
# explicitly target the .a to avoid pulling in any residual link
53+
# targets that would fail with an undefined reference.
54+
- name: Build wolfSSL static library
55+
run: make -j$(nproc) src/libwolfssl.la
5156

5257
- name: Build emNET non-blocking test
5358
run: make -C tests/emnet

.wolfssl_known_macro_extras

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,6 @@ WOLFSSL_ECDSA_SET_K_ONE_LOOP
760760
WOLFSSL_EC_POINT_CMP_JACOBIAN
761761
WOLFSSL_ED448_NO_LARGE_CODE
762762
WOLFSSL_EDDSA_CHECK_PRIV_ON_SIGN
763-
WOLFSSL_EMNET
764763
WOLFSSL_ESPWROOM32
765764
WOLFSSL_EVP_PRINT
766765
WOLFSSL_EXPORT_INT

src/wolfio.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,24 @@ static WC_INLINE int wolfSSL_LastError(int err, SOCKET_T sd)
154154
#elif defined(WOLFSSL_LINUXKM)
155155
return -err; /* Return provided error value with corrected sign. */
156156
#elif defined(WOLFSSL_EMNET)
157-
/* emNET BSD sockets return the IP_ERR_* value (negative) directly
158-
* from send/recv on failure; no translation needed. */
157+
/* Any negative recv/send return is a SOCKET_ERROR sentinel under
158+
* emNET; the canonical IP_ERR_* lives in the socket SO_ERROR.
159+
* Retrieving it via IP_SOCK_getsockopt works across both emNET
160+
* integrator conventions (native: recv returns IP_ERR_* directly;
161+
* POSIX facade: recv returns -1 with errno set). If the lookup
162+
* itself fails, fall back to IP_ERR_FAULT rather than returning
163+
* the raw -1 sentinel - the latter matches no SOCKET_E* constant
164+
* and would regress into WOLFSSL_CBIO_ERR_GENERAL. */
165+
if (err < 0) {
166+
int sock_err = err;
167+
if (IP_SOCK_getsockopt(sd, SOL_SOCKET, SO_ERROR, &sock_err,
168+
(int)sizeof(sock_err)) == 0) {
169+
err = sock_err;
170+
}
171+
else if (err == -1) {
172+
err = IP_ERR_FAULT;
173+
}
174+
}
159175
return err;
160176
#elif defined(FUSION_RTOS)
161177
#include <fclerrno.h>

tests/emnet/IP/IP.h

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
* source.
55
*
66
* Scope: enough to build wolfSSL with -DWOLFSSL_EMNET on a POSIX host
7-
* for CI test purposes. Only error constants and IP_SOCK_getsockopt are
8-
* provided here; the runtime behaviour of send/recv under emNET is
9-
* emulated by emnet_shim.c via linker --wrap.
7+
* for CI test purposes. Only error constants and IP_SOCK_getsockopt
8+
* are provided here; send/recv fall through to glibc's POSIX
9+
* implementation, and the canonical IP_ERR_* lookup is emulated by
10+
* IP_SOCK_getsockopt in emnet_shim.c.
1011
*/
1112

1213
#ifndef WOLFSSL_EMNET_SHIM_IP_H
@@ -32,10 +33,11 @@ extern "C" {
3233
#define IP_ERR_PIPE (-13)
3334
#define IP_ERR_FAULT (-25)
3435

35-
/* BSD-style socket option retrieval. Signature matches the SEGGER API:
36-
* length is passed by pointer of type int*, unlike POSIX socklen_t*. */
37-
int IP_SOCK_getsockopt(int sd, int level, int optname,
38-
void *optval, int *optlen);
36+
/* BSD-style socket option retrieval. Signature matches the SEGGER
37+
* emNET API (UM07001, IP_Socket.h): the length is passed by value as
38+
* a plain int, not a POSIX socklen_t* / int*. */
39+
int IP_SOCK_getsockopt(int hSock, int Level, int Name,
40+
void *pVal, int ValLen);
3941

4042
#ifdef __cplusplus
4143
}

tests/emnet/Makefile

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@
99
# -I$$(pwd)/tests/emnet"
1010
# make
1111
# make -C tests/emnet run
12+
#
13+
# recv/send fall through to glibc's POSIX implementation (-1 + errno
14+
# on would-block); wolfSSL_LastError then retrieves the canonical
15+
# IP_ERR_* via IP_SOCK_getsockopt in the shim. This is the integrator
16+
# convention that reproduces ticket #21673 and the one wolfSSL's
17+
# WOLFSSL_EMNET error path must handle correctly.
1218

1319
CURDIR := $(shell pwd)
1420
WOLFSSL_ROOT := $(abspath $(CURDIR)/../..)
@@ -19,7 +25,6 @@ CFLAGS_TEST := -Wall -Wextra -O2 -g \
1925
-DWOLFSSL_EMNET \
2026
-I$(CURDIR) \
2127
-I$(WOLFSSL_ROOT)
22-
LDFLAGS_TEST := -Wl,--wrap=recv,--wrap=send
2328
LIBS_TEST := $(WOLFSSL_LIB) -lpthread -lm
2429

2530
TEST_BIN := emnet_nonblock_test
@@ -35,8 +40,10 @@ check-lib:
3540
exit 1; \
3641
fi
3742

38-
$(TEST_BIN): emnet_nonblock_test.o emnet_shim.o check-lib
39-
$(CC) $(LDFLAGS_TEST) -o $@ emnet_nonblock_test.o emnet_shim.o $(LIBS_TEST)
43+
# check-lib is an order-only dependency (after |) so its phony status
44+
# does not force the binary to relink on every `make` invocation.
45+
$(TEST_BIN): emnet_nonblock_test.o emnet_shim.o | check-lib
46+
$(CC) -o $@ emnet_nonblock_test.o emnet_shim.o $(LIBS_TEST)
4047

4148
emnet_nonblock_test.o: emnet_nonblock_test.c
4249
$(CC) $(CFLAGS_TEST) -c $< -o $@

tests/emnet/emnet_nonblock_test.c

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
/* emnet_nonblock_test.c -- non-blocking TLS 1.3 handshake over a
2-
* socketpair, with wolfSSL built for WOLFSSL_EMNET and the recv/send
3-
* error surface translated by emnet_shim.c.
2+
* socketpair, with wolfSSL built for WOLFSSL_EMNET and the canonical
3+
* IP_ERR_* lookup provided by IP_SOCK_getsockopt in emnet_shim.c.
4+
*
5+
* recv/send fall through to glibc's POSIX implementation, returning
6+
* -1 with errno set on would-block. This reproduces the integrator
7+
* convention in ticket #21673, where wolfSSL_LastError must consult
8+
* IP_SOCK_getsockopt(SO_ERROR) rather than trusting the raw recv/send
9+
* return value.
410
*
511
* Asserts the steady-state contract of wolfSSL's WOLFSSL_EMNET path:
612
* when the underlying socket would block, wolfSSL_get_error returns
713
* WOLFSSL_ERROR_WANT_READ / WOLFSSL_ERROR_WANT_WRITE and the handshake
8-
* completes without spurious fatal errors. Guards against regressions
9-
* of a prior bug where the WOLFSSL_EMNET branch in wolfSSL_LastError()
10-
* was shadowed by a combined WOLFSSL_LINUXKM||WOLFSSL_EMNET arm that
11-
* inverted the sign of IP_ERR_WOULD_BLOCK, causing TranslateIoReturnCode
12-
* to surface WOLFSSL_CBIO_ERR_GENERAL on would-block.
14+
* completes without spurious fatal errors.
1315
*/
1416

1517
#include <errno.h>
@@ -61,8 +63,13 @@ static void set_nonblock(int fd)
6163
* success (ready or timeout, caller retries), -1 on hard failure. */
6264
static int wait_io(struct side *s, short events, int iter)
6365
{
64-
struct pollfd pfd = { .fd = s->fd, .events = events };
65-
int r = poll(&pfd, 1, POLL_MS);
66+
struct pollfd pfd;
67+
int r;
68+
69+
pfd.fd = s->fd;
70+
pfd.events = events;
71+
pfd.revents = 0;
72+
r = poll(&pfd, 1, POLL_MS);
6673
if (r >= 0)
6774
return 0;
6875
if (errno == EINTR)
@@ -76,15 +83,17 @@ static void *run_side(void *arg)
7683
{
7784
struct side *s = (struct side *)arg;
7885
int iter;
86+
int ret;
87+
int err;
7988

8089
for (iter = 0; iter < MAX_ITERS; iter++) {
81-
int ret = s->fn(s->ssl);
90+
ret = s->fn(s->ssl);
8291
if (ret == WOLFSSL_SUCCESS) {
8392
s->completed = 1;
8493
return NULL;
8594
}
8695

87-
int err = wolfSSL_get_error(s->ssl, ret);
96+
err = wolfSSL_get_error(s->ssl, ret);
8897
s->last_err = err;
8998

9099
if (err == WOLFSSL_ERROR_WANT_READ) {
@@ -125,6 +134,16 @@ static void *run_side(void *arg)
125134
int main(void)
126135
{
127136
int sv[2];
137+
WOLFSSL_CTX *sctx;
138+
WOLFSSL_CTX *cctx;
139+
WOLFSSL *server_ssl;
140+
WOLFSSL *client_ssl;
141+
struct side server;
142+
struct side client;
143+
pthread_t st, ct;
144+
int prc;
145+
int rc;
146+
128147
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0) {
129148
perror("socketpair");
130149
return 2;
@@ -134,8 +153,8 @@ int main(void)
134153

135154
wolfSSL_Init();
136155

137-
WOLFSSL_CTX *sctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method());
138-
WOLFSSL_CTX *cctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
156+
sctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method());
157+
cctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
139158
if (!sctx || !cctx) {
140159
fprintf(stderr, "wolfSSL_CTX_new failed\n");
141160
return 2;
@@ -157,8 +176,8 @@ int main(void)
157176
return 2;
158177
}
159178

160-
WOLFSSL *server_ssl = wolfSSL_new(sctx);
161-
WOLFSSL *client_ssl = wolfSSL_new(cctx);
179+
server_ssl = wolfSSL_new(sctx);
180+
client_ssl = wolfSSL_new(cctx);
162181
if (!server_ssl || !client_ssl) {
163182
fprintf(stderr, "wolfSSL_new failed\n");
164183
return 2;
@@ -167,13 +186,17 @@ int main(void)
167186
wolfSSL_set_fd(server_ssl, sv[0]);
168187
wolfSSL_set_fd(client_ssl, sv[1]);
169188

170-
struct side server = { .fd = sv[0], .name = "server",
171-
.ssl = server_ssl, .fn = wolfSSL_accept };
172-
struct side client = { .fd = sv[1], .name = "client",
173-
.ssl = client_ssl, .fn = wolfSSL_connect };
189+
memset(&server, 0, sizeof(server));
190+
memset(&client, 0, sizeof(client));
191+
server.fd = sv[0];
192+
server.name = "server";
193+
server.ssl = server_ssl;
194+
server.fn = wolfSSL_accept;
195+
client.fd = sv[1];
196+
client.name = "client";
197+
client.ssl = client_ssl;
198+
client.fn = wolfSSL_connect;
174199

175-
pthread_t st, ct;
176-
int prc;
177200
prc = pthread_create(&st, NULL, run_side, &server);
178201
if (prc != 0) {
179202
fprintf(stderr, "FAIL: pthread_create(server): %s\n", strerror(prc));
@@ -196,7 +219,7 @@ int main(void)
196219
return 2;
197220
}
198221

199-
int rc = 0;
222+
rc = 0;
200223
if (server.failed || client.failed) {
201224
rc = 1;
202225
} else if (!server.completed || !client.completed) {

tests/emnet/emnet_shim.c

Lines changed: 39 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,12 @@
11
/* emnet_shim.c -- POSIX-backed shim for the emNET (embOS/IP) socket ABI
22
* used by wolfSSL when WOLFSSL_EMNET is defined.
33
*
4-
* The goal is to reproduce the error-reporting contract of emNET on top
5-
* of stock Linux BSD sockets: when the underlying socket signals
6-
* would-block, connection reset, etc., the shim surfaces the
7-
* corresponding IP_ERR_* negative constant (emNET convention) instead
8-
* of -1/errno (POSIX convention). This is exactly what wolfSSL's
9-
* WOLFSSL_EMNET branch in wolfio.h/wolfio.c was written to consume, so
10-
* CI can drive the non-blocking handshake paths without the real
11-
* SEGGER stack.
12-
*
13-
* Linker wrapping:
14-
* -Wl,--wrap=recv,--wrap=send
15-
* hooks wolfSSL's RECV_FUNCTION/SEND_FUNCTION (which are the
16-
* unqualified POSIX send/recv on the WOLFSSL_EMNET build) without
17-
* patching any wolfSSL source.
4+
* Provides the canonical error lookup path wolfSSL's wolfSSL_LastError
5+
* relies on: IP_SOCK_getsockopt(SO_ERROR) returns the pending IP_ERR_*
6+
* for a socket, as required by UM07001's emNET API contract. On top of
7+
* Linux BSD sockets this is emulated by consulting POSIX SO_ERROR plus
8+
* the thread-local errno (because Linux does not store transient
9+
* would-block conditions in SO_ERROR).
1810
*/
1911

2012
#include "IP/IP.h"
@@ -25,10 +17,6 @@
2517
#include <stddef.h>
2618
#include <string.h>
2719

28-
/* Forward declarations for the linker's --wrap mechanism. */
29-
ssize_t __real_recv(int sd, void *buf, size_t len, int flags);
30-
ssize_t __real_send(int sd, const void *buf, size_t len, int flags);
31-
3220
/* Translate a POSIX errno value into the emNET IP_ERR_* space. */
3321
static int emnet_errno_to_ip_err(int err)
3422
{
@@ -50,54 +38,45 @@ static int emnet_errno_to_ip_err(int err)
5038
}
5139
}
5240

53-
/* recv wrapper: preserve success/close semantics; on error return the
54-
* emNET-style negative error code in place of -1/errno. wolfSSL's
55-
* TranslateIoReturnCode uses err < 0 to branch into error handling and
56-
* then compares against SOCKET_EWOULDBLOCK == IP_ERR_WOULD_BLOCK. */
57-
ssize_t __wrap_recv(int sd, void *buf, size_t len, int flags)
41+
/* IP_SOCK_getsockopt: emulates the emNET ABI on top of POSIX. This is
42+
* the canonical error source for WOLFSSL_EMNET code paths - wolfSSL
43+
* calls it after a negative recv/send return to retrieve the real
44+
* IP_ERR_* value.
45+
*
46+
* For SO_ERROR we deliberately diverge from a pure POSIX pass-through:
47+
* Linux stores sticky socket errors (ECONNRESET etc.) in SO_ERROR but
48+
* does NOT store transient would-block conditions (EAGAIN/EWOULDBLOCK)
49+
* there - those live only in thread-local errno after the failing
50+
* syscall. Real emNET's SO_ERROR does carry would-block. To reproduce
51+
* that contract here, read POSIX SO_ERROR first, fall back to errno
52+
* when SO_ERROR is empty, then translate into the IP_ERR_* space. */
53+
int IP_SOCK_getsockopt(int hSock, int Level, int Name,
54+
void *pVal, int ValLen)
5855
{
59-
ssize_t ret = __real_recv(sd, buf, len, flags);
60-
if (ret < 0) {
61-
return (ssize_t)emnet_errno_to_ip_err(errno);
56+
if (pVal == NULL || ValLen <= 0) {
57+
errno = EINVAL;
58+
return -1;
6259
}
63-
return ret;
64-
}
6560

66-
ssize_t __wrap_send(int sd, const void *buf, size_t len, int flags)
67-
{
68-
ssize_t ret = __real_send(sd, buf, len, flags);
69-
if (ret < 0) {
70-
return (ssize_t)emnet_errno_to_ip_err(errno);
71-
}
72-
return ret;
73-
}
61+
if (Level == SOL_SOCKET && Name == SO_ERROR
62+
&& ValLen >= (int)sizeof(int)) {
63+
int saved_errno = errno;
64+
int so_err = 0;
65+
socklen_t posix_len = (socklen_t)sizeof(so_err);
66+
int ip_err;
7467

75-
/* IP_SOCK_getsockopt: kept to satisfy the emNET ABI surface expected
76-
* by WOLFSSL_EMNET-linked code. Delegates to POSIX getsockopt and, for
77-
* SO_ERROR, maps the returned POSIX errno value into emNET's IP_ERR_*
78-
* space so callers see emNET-style error reporting. */
79-
int IP_SOCK_getsockopt(int sd, int level, int optname,
80-
void *optval, int *optlen)
81-
{
82-
socklen_t posix_len;
83-
int rc;
68+
(void)getsockopt(hSock, SOL_SOCKET, SO_ERROR, &so_err, &posix_len);
69+
if (so_err == 0)
70+
so_err = saved_errno;
8471

85-
if (optlen == NULL) {
86-
errno = EINVAL;
87-
return -1;
72+
ip_err = emnet_errno_to_ip_err(so_err);
73+
memcpy(pVal, &ip_err, sizeof(ip_err));
74+
return 0;
8875
}
89-
posix_len = (socklen_t)*optlen;
90-
rc = getsockopt(sd, level, optname, optval, &posix_len);
91-
*optlen = (int)posix_len;
9276

93-
if (rc == 0 && level == SOL_SOCKET && optname == SO_ERROR
94-
&& optval != NULL && posix_len >= (socklen_t)sizeof(int)) {
95-
int so_err;
96-
memcpy(&so_err, optval, sizeof(so_err));
97-
if (so_err != 0) {
98-
so_err = emnet_errno_to_ip_err(so_err);
99-
memcpy(optval, &so_err, sizeof(so_err));
100-
}
77+
/* Pass-through for other options. */
78+
{
79+
socklen_t posix_len = (socklen_t)ValLen;
80+
return getsockopt(hSock, Level, Name, pVal, &posix_len);
10181
}
102-
return rc;
10382
}

0 commit comments

Comments
 (0)