From bc6c1a15de299a5cf824657913ef2fa81b9ca799 Mon Sep 17 00:00:00 2001 From: Paul Guyot Date: Sat, 18 Jul 2026 08:03:58 +0200 Subject: [PATCH] Validate USB CDC port request terms before dispatch The rp2 and stm32 usb_cdc drivers indexed the $gen_call request term as a tuple without checking its shape, so an immediate could be dereferenced as a boxed pointer. Validate tuple and arity before indexing, and harden the rp2 create_port OOM path to unwind instead of aborting. Signed-off-by: Paul Guyot --- src/platforms/rp2/src/lib/usb_cdc_driver.c | 44 ++++++++++++++++---- src/platforms/stm32/src/lib/usb_cdc_driver.c | 14 +++++++ 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/platforms/rp2/src/lib/usb_cdc_driver.c b/src/platforms/rp2/src/lib/usb_cdc_driver.c index 2569dda785..ef62e65689 100644 --- a/src/platforms/rp2/src/lib/usb_cdc_driver.c +++ b/src/platforms/rp2/src/lib/usb_cdc_driver.c @@ -47,6 +47,7 @@ #include "rp2_sys.h" static NativeHandlerResult usb_cdc_driver_consume_mailbox(Context *ctx); +static void usb_cdc_send_error_reply(Context *ctx, term pid, term ref, AtomString reason_atom_str); #define USB_CDC_BUF_SIZE 256 #define NO_REF 0 @@ -187,13 +188,32 @@ static Context *usb_cdc_driver_create_port(GlobalContext *global, term opts) return NULL; } - Context *ctx = context_new(global); - struct USBCDCData *cdc_data = malloc(sizeof(struct USBCDCData)); if (IS_NULL_PTR(cdc_data)) { SMP_MUTEX_UNLOCK(s_open_lock); fprintf(stderr, "Failed to allocate memory: %s:%i.\n", __FILE__, __LINE__); - AVM_ABORT(); + return NULL; + } + +#ifndef AVM_NO_SMP + cdc_data->reader_lock = smp_mutex_create(); + if (IS_NULL_PTR(cdc_data->reader_lock)) { + free(cdc_data); + SMP_MUTEX_UNLOCK(s_open_lock); + fprintf(stderr, "Failed to allocate memory: %s:%i.\n", __FILE__, __LINE__); + return NULL; + } +#endif + + Context *ctx = context_new(global); + if (IS_NULL_PTR(ctx)) { +#ifndef AVM_NO_SMP + smp_mutex_destroy(cdc_data->reader_lock); +#endif + free(cdc_data); + SMP_MUTEX_UNLOCK(s_open_lock); + fprintf(stderr, "Failed to allocate memory: %s:%i.\n", __FILE__, __LINE__); + return NULL; } queue_init(&cdc_data->rxqueue, sizeof(uint32_t), EVENT_QUEUE_LEN); @@ -204,10 +224,6 @@ static Context *usb_cdc_driver_create_port(GlobalContext *global, term opts) cdc_data->reader_ref_ticks = 0; cdc_data->itf = 0; -#ifndef AVM_NO_SMP - cdc_data->reader_lock = smp_mutex_create(); -#endif - // TinyUSB should already be initialized by Pico SDK when // pico_enable_stdio_usb is set. For distribution use, stdio_usb // should be disabled and this driver takes over the CDC interface. @@ -295,6 +311,10 @@ static void usb_cdc_driver_do_cancel_read(Context *ctx, GenMessage gen_message) bool clear_unconditionally = term_is_atom(req); uint64_t target_ref_ticks = 0; if (!clear_unconditionally) { + if (UNLIKELY(term_get_tuple_arity(req) < 2)) { + usb_cdc_send_error_reply(ctx, pid, ref, ATOM_STR("\x6", "badarg")); + return; + } target_ref_ticks = term_to_ref_ticks(term_get_tuple_element(req, 1)); } @@ -333,6 +353,10 @@ static void usb_cdc_driver_do_write(Context *ctx, GenMessage gen_message) term pid = gen_message.pid; term ref = gen_message.ref; + if (UNLIKELY(!term_is_tuple(msg) || term_get_tuple_arity(msg) < 2)) { + usb_cdc_send_error_reply(ctx, pid, ref, ATOM_STR("\x6", "badarg")); + return; + } term data = term_get_tuple_element(msg, 1); int local_pid = term_to_local_process_id(pid); @@ -530,6 +554,12 @@ static NativeHandlerResult usb_cdc_driver_consume_mailbox(Context *ctx) } term req = gen_message.req; + // req is unvalidated: an immediate would be misread as a boxed tuple pointer below. + if (UNLIKELY(!term_is_atom(req) && !(term_is_tuple(req) && term_get_tuple_arity(req) >= 1))) { + usb_cdc_send_error_reply(ctx, gen_message.pid, gen_message.ref, ATOM_STR("\xF", "unknown_command")); + mailbox_remove_message(&ctx->mailbox, &ctx->heap); + continue; + } term cmd_term = term_is_atom(req) ? req : term_get_tuple_element(req, 0); enum usb_cdc_cmd cmd = interop_atom_term_select_int(cmd_table, cmd_term, ctx->global); diff --git a/src/platforms/stm32/src/lib/usb_cdc_driver.c b/src/platforms/stm32/src/lib/usb_cdc_driver.c index a5d9af0672..40e6276641 100644 --- a/src/platforms/stm32/src/lib/usb_cdc_driver.c +++ b/src/platforms/stm32/src/lib/usb_cdc_driver.c @@ -252,6 +252,10 @@ static void usb_cdc_driver_do_cancel_read(Context *ctx, GenMessage gen_message) bool clear_unconditionally = term_is_atom(req); uint64_t target_ref_ticks = 0; if (!clear_unconditionally) { + if (UNLIKELY(term_get_tuple_arity(req) < 2)) { + usb_cdc_send_error_reply(ctx, pid, ref, ATOM_STR("\x6", "badarg")); + return; + } target_ref_ticks = term_to_ref_ticks(term_get_tuple_element(req, 1)); } @@ -276,6 +280,10 @@ static void usb_cdc_driver_do_write(Context *ctx, GenMessage gen_message) term pid = gen_message.pid; term ref = gen_message.ref; + if (UNLIKELY(!term_is_tuple(msg) || term_get_tuple_arity(msg) < 2)) { + usb_cdc_send_error_reply(ctx, pid, ref, ATOM_STR("\x6", "badarg")); + return; + } term data = term_get_tuple_element(msg, 1); int local_pid = term_to_local_process_id(pid); @@ -458,6 +466,12 @@ static NativeHandlerResult usb_cdc_driver_consume_mailbox(Context *ctx) } term req = gen_message.req; + // req is unvalidated: an immediate would be misread as a boxed tuple pointer below. + if (UNLIKELY(!term_is_atom(req) && !(term_is_tuple(req) && term_get_tuple_arity(req) >= 1))) { + usb_cdc_send_error_reply(ctx, gen_message.pid, gen_message.ref, ATOM_STR("\xF", "unknown_command")); + mailbox_remove_message(&ctx->mailbox, &ctx->heap); + continue; + } term cmd_term = term_is_atom(req) ? req : term_get_tuple_element(req, 0); enum usb_cdc_cmd cmd = interop_atom_term_select_int(cmd_table, cmd_term, ctx->global);