Skip to content

[BUG] nrf91_modem_sock: missing arglen validation in USRSOCK_REQUEST_IOCTL leads to OOB read/write #18515

@XueDugu

Description

@XueDugu

Description / Steps to reproduce the issue

In arch/arm/src/nrf91/nrf91_modem_sock.c, nrf91_usrsock_ioctl_handler() trusts the arglen field of a
USRSOCK_REQUEST_IOCTL message and copies req->arglen bytes from the request payload into the fixed-size
usrsock->out buffer without validating either the actual request length or the destination capacity:

ack = (struct usrsock_message_datareq_ack_s *)usrsock->out;
memcpy(ack + 1, req + 1, req->arglen);

Because arglen is derived from untrusted input, a process able to submit a crafted usrsock ioctl request can
trigger:

  1. an out-of-bounds read from the input buffer when arglen exceeds the number of bytes actually present after
    the request header; and
  2. an out-of-bounds write into g_usrsock.out when arglen exceeds the remaining space after the ack header.

The copied data is later returned through nrf91_usrsock_send_dack(), so the first case can disclose memory
beyond the received request, while the second case can corrupt adjacent fields in struct nrf91_usrsock_s.

Vulnerability details

usrsock_request() copies request data into g_usrsock.in and passes the resulting byte count (len) to
nrf91_usrsock_ioctl_handler(). The handler immediately casts the buffer and dereferences request fields without
first verifying that the full header is present, and it does not check that req->arglen fits either the received
request or the fixed-size output buffer.

Relevant code path:

static int nrf91_usrsock_ioctl_handler(struct nrf91_usrsock_s *usrsock,
const void *data, size_t len)
{
const struct usrsock_request_ioctl_s *req = data;
struct usrsock_message_datareq_ack_s *ack = NULL;
int ret = 0;

ack = (struct usrsock_message_datareq_ack_s *)usrsock->out;
memcpy(ack + 1, req + 1, req->arglen);
ret = nrf91_usrsock_ioctl(req->usockid,
                          req->cmd,
                          (unsigned long)(ack + 1));

return nrf91_usrsock_send_dack(usrsock, ack, req->head.xid, ret,
                               req->arglen, req->arglen);

}

Missing validations:

  1. len >= sizeof(*req) before reading req->...
  2. req->arglen <= len - sizeof(*req) to ensure the payload is fully present
  3. req->arglen <= NRF91_USRSOCK_BUFSIZE - sizeof(*ack) to ensure the destination buffer is large enough

Issue 1: Out-of-bounds read

If req->arglen > len - sizeof(*req), memcpy() reads past the end of the received request buffer. Because the
handler later sends a data ack using the same arglen value, this can disclose memory adjacent to g_usrsock.in.

Issue 2: Out-of-bounds write

g_usrsock.out is fixed at NRF91_USRSOCK_BUFSIZE (4096 bytes). Since ack points to the start of this buffer, the
maximum safe copy size is:

NRF91_USRSOCK_BUFSIZE - sizeof(struct usrsock_message_datareq_ack_s)

If req->arglen exceeds this limit, memcpy() writes past g_usrsock.out and corrupts adjacent fields in struct
nrf91_usrsock_s.

Steps to reproduce

  1. Build and run NuttX on an nRF91 target with the modem/usrsock path enabled.
  2. Obtain the ability to submit a USRSOCK_REQUEST_IOCTL message to the usrsock interface.
  3. For the OOB read case, send a request whose arglen is larger than the number of bytes actually supplied after
    struct usrsock_request_ioctl_s.
  4. For the OOB write case, send a request whose arglen is larger than NRF91_USRSOCK_BUFSIZE - sizeof(struct
    usrsock_message_datareq_ack_s); for example, 0xffff.
  5. Observe memory disclosure in the returned data ack and/or corruption of adjacent fields in struct
    nrf91_usrsock_s.

Suggested fix

static int nrf91_usrsock_ioctl_handler(struct nrf91_usrsock_s *usrsock,
const void *data, size_t len)
{
const struct usrsock_request_ioctl_s *req = data;
struct usrsock_message_datareq_ack_s *ack;
int ret;

if (len < sizeof(*req))
  {
    return -EINVAL;
  }

if (req->arglen > len - sizeof(*req))
  {
    return -EINVAL;
  }

ack = (struct usrsock_message_datareq_ack_s *)usrsock->out;

if (req->arglen > NRF91_USRSOCK_BUFSIZE - sizeof(*ack))
  {
    return -EINVAL;
  }

memcpy(ack + 1, req + 1, req->arglen);

ret = nrf91_usrsock_ioctl(req->usockid, req->cmd,
                          (unsigned long)(ack + 1));

return nrf91_usrsock_send_dack(usrsock, ack, req->head.xid, ret,
                               req->arglen, req->arglen);

}

Impact

A local actor able to submit crafted usrsock ioctl requests can trigger out-of-bounds reads and writes in the
nRF91 usrsock handler. This can lead to memory disclosure, corruption of adjacent usrsock state, denial of
service, and potentially further exploitation depending on build configuration and runtime memory layout.

On which OS does this issue occur?

[OS: Windows], [OS: Linux]

What is the version of your OS?

Ubuntu 22.04.5 LTS

NuttX Version

master

Issue Architecture

[Arch: arm]

Issue Area

[Area: Networking]

Host information

This is a source-level vulnerability in arch/arm/src/nrf91/nrf91_modem_sock.c and is not dependent on a
host-specific build environment. Confirmed by code inspection on Apache NuttX master and nuttx-12.12.0.

Verification

  • I have verified before submitting the report.

Metadata

Metadata

Assignees

Labels

Arch: armIssues related to ARM (32-bit) architectureArea: NetworkingEffects networking subsystemOS: LinuxIssues related to Linux (building system, etc)OS: WindowsIssues related to Windows (building system, etc)Type: BugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions