Skip to content

Commit

Permalink
Work around more Linux kernel ABI breakage
Browse files Browse the repository at this point in the history
Back in Change-Id Ia95dd1f9ffed9f743c049e05797b1a6f1f9f8c69 we tried
to work-around kernel ABI breakage introduced in kernel >= v5.5, but it
seems that there have already been similar ABI breakages in v4.11 and
v4.12.

See
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/include/uapi/linux/sctp.h?id=b6e6b5f1da7e8d092f86a4351802c27c0170c5a5
and https://marc.info/?l=linux-sctp&m=158729301516157&w=2 for the most
recent incarnation.

See https://osmocom.org/issues/4573#note-6 for all known cases
of SCTP_EVENTS ABI breakage.

Copied from https://git.osmocom.org/libosmo-netif/
commit 157ad6d7075ad56038c984ebf8634f1bc7c6c59c ff.

Related: OS#5366
  • Loading branch information
Daniel Willmann committed Dec 22, 2021
1 parent a21b94f commit 2e911c3
Showing 1 changed file with 78 additions and 1 deletion.
79 changes: 78 additions & 1 deletion src/SCTPasp_PT.cc
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,83 @@

namespace SCTPasp__PortType {

/* is any of the bytes from offset .. u8_size in 'u8' non-zero? return offset or -1 if all zero */
static int byte_nonzero(const uint8_t *u8, unsigned int offset, unsigned int u8_size)
{
int j;
for (j = offset; j < u8_size; j++) {
if (u8[j] != 0)
return j;
}
return -1;
}
static int sctp_sockopt_event_subscribe_size = 0;
static int determine_sctp_sockopt_event_subscribe_size(void)
{
uint8_t buf[256];
socklen_t buf_len = sizeof(buf);
int sd, rc;
/* only do this once */
if (sctp_sockopt_event_subscribe_size != 0)
return 0;
sd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
if (sd < 0)
return sd;
rc = getsockopt(sd, IPPROTO_SCTP, SCTP_EVENTS, buf, &buf_len);
close(sd);
if (rc < 0)
return rc;
sctp_sockopt_event_subscribe_size = buf_len;
//LOGP(DLINP, LOGL_INFO, "sizes of 'struct sctp_event_subscribe': compile-time %zu, kernel: %u\n",
// sizeof(struct sctp_event_subscribe), sctp_sockopt_event_subscribe_size);
return 0;
}
/* Attempt to work around Linux kernel ABI breakage
*
* The Linux kernel ABI for the SCTP_EVENTS socket option has been broken repeatedly.
* - until commit 35ea82d611da59f8bea44a37996b3b11bb1d3fd7 ( kernel < 4.11), the size is 10 bytes
* - in 4.11 it is 11 bytes
* - in 4.12 .. 5.4 it is 13 bytes
* - in kernels >= 5.5 it is 14 bytes
*
* This wouldn't be a problem if the kernel didn't have a "stupid" assumption that the structure
* size passed by userspace will match 1:1 the length of the structure at kernel compile time. In
* an ideal world, it would just use the known first bytes and assume the remainder is all zero.
* But as it doesn't do that, let's try to work around this */
static int sctp_setsockopt_events_linux_workaround(int fd, const struct sctp_event_subscribe *event)
{
const unsigned int compiletime_size = sizeof(*event);
int rc;
if (determine_sctp_sockopt_event_subscribe_size() < 0) {
//LOGP(DLINP, LOGL_ERROR, "Cannot determine SCTP_EVENTS socket option size\n");
return -1;
}
if (compiletime_size == sctp_sockopt_event_subscribe_size) {
/* no kernel workaround needed */
return setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, event, compiletime_size);
} else if (compiletime_size < sctp_sockopt_event_subscribe_size) {
/* we are using an older userspace with a more modern kernel and hence need
* to pad the data */
uint8_t buf[sctp_sockopt_event_subscribe_size];
memcpy(buf, event, compiletime_size);
memset(buf + sizeof(*event), 0, sctp_sockopt_event_subscribe_size - compiletime_size);
return setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, buf, sctp_sockopt_event_subscribe_size);
} else /* if (compiletime_size > sctp_sockopt_event_subscribe_size) */ {
/* we are using a newer userspace with an older kernel and hence need to truncate
* the data - but only if the caller didn't try to enable any of the events of the
* truncated portion */
rc = byte_nonzero((const uint8_t *)event, sctp_sockopt_event_subscribe_size,
compiletime_size);
if (rc >= 0) {
//LOGP(DLINP, LOGL_ERROR, "Kernel only supports sctp_event_subscribe of %u bytes, "
// "but caller tried to enable more modern event at offset %u\n",
// sctp_sockopt_event_subscribe_size, rc);
return -1;
}
return setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, event, sctp_sockopt_event_subscribe_size);
}
}

struct SCTPasp__PT_PROVIDER::fd_map_item
{ // used by map operations
int fd; // socket descriptor
Expand Down Expand Up @@ -1568,7 +1645,7 @@ int SCTPasp__PT_PROVIDER::create_socket(int addr_family)
}

log("Setting SCTP socket options (events).");
if (setsockopt(local_fd, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof (events)) < 0)
if (sctp_setsockopt_events_linux_workaround(local_fd, &events) < 0)
{
TTCN_warning("Setsockopt error!");
errno = 0;
Expand Down

0 comments on commit 2e911c3

Please sign in to comment.