Skip to content

Commit 46016cb

Browse files
lrgirdwoclaude
andcommitted
ipc4: handler: bound get_large_config reply size to the reply buffer
The IPC4 fuzzer (simple-IPC-fuzz_sh) aborts with an AddressSanitizer stack-overflow report in the ipc_send_wq thread, inside vfprintf() under posix_print_trace(). That report is a red herring: the real failure is an assert, and the recursive assert -> k_panic -> assert loop that follows it is what eventually exhausts the 8MB pthread stack native_sim gives the thread. With -jobs>1 the harness passes -close_fd_mask=1, so printk (which goes to stdout) is discarded and only the secondary ASan report on stderr survives into the CI log. The assert is: ASSERTION FAIL [!dsp_write_err] @ sof/src/include/sof/lib/mailbox.h:52 reached from ipc_send_queued_msg() -> ipc_platform_send_msg() -> mailbox_dspbox_write(0, msg->tx_data, 918016), i.e. a reply claiming ~900KB of payload for a 4KB mailbox. ipc4_get_large_config_module_instance() seeds data_offset from config->extension.r.data_off_size, a 20-bit host-controlled field, and then passes it to drv->ops.get_large_config() as an in/out parameter. A module whose .get_configuration produces no data (template_get_config() is the one the fuzzer found, but any IPC3-oriented stub behaves the same) returns success without writing *data_offset_size, so the host-supplied value survives unchanged and is published as msg_reply->tx_size. The payload itself lives in ipc->comp_data, which is only SOF_IPC_MSG_MAX_SIZE bytes. Only the VENDOR_CONFIG_PARAM branch bounds data_off_size today, and it does so for the inbound hostbox read, not for the reply. With asserts compiled out, memcpy_s() rejects the copy but the return value is discarded and dcache_writeback_region() is still asked to write back the out-of-range length, so the caller sends a reply header advertising a size that was never copied. Bound the reply size against the space actually left in the reply buffer before it is published, and fail the command with IPC4_INVALID_CONFIG_DATA_LEN otherwise. The bound is computed from data rather than from SOF_IPC_MSG_MAX_SIZE directly because the non-vendor branch advances data by sizeof(reply) under CONFIG_LIBRARY. The same fix is applied to ipc4_process_large_config_get(), the CONFIG_SOF_USERSPACE_LL variant, which has the identical flaw. Reproduced and verified on native_sim/native/64 with the crash artifact from the failing CI run (thesofproject/sof PR 11193, job simple-IPC-fuzz_sh (4)): it aborts before the change and exits 0 after it. A subsequent 420s/8-job fuzz run over a ~10k-input corpus produced no new artifacts, and an intel_adsp/ace30/ptl cross-build is clean. Signed-off-by: Liam Girdwood <liam.r.girdwood@linux.intel.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 52dd10e commit 46016cb

1 file changed

Lines changed: 32 additions & 0 deletions

File tree

‎src/ipc/ipc4/handler-user.c‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,7 @@ __cold int ipc4_process_large_config_get(struct ipc4_message_request *ipc4,
11031103
const struct comp_driver *drv;
11041104
struct comp_dev *dev = NULL;
11051105
uint32_t data_offset;
1106+
size_t data_max;
11061107
int ret;
11071108

11081109
assert_can_be_cold();
@@ -1167,6 +1168,21 @@ __cold int ipc4_process_large_config_get(struct ipc4_message_request *ipc4,
11671168
if (ret < 0)
11681169
ret = IPC4_MOD_INVALID_ID;
11691170

1171+
/* data_offset is now the reply payload size, but it was seeded from
1172+
* the host-controlled 20-bit data_off_size field and a module that
1173+
* produces no data leaves it untouched, so it can still claim up to
1174+
* ~1MB. The payload lives in ipc->comp_data (SOF_IPC_MSG_MAX_SIZE
1175+
* bytes) and the sender copies tx_size bytes of it into the DSP
1176+
* mailbox, so reject anything that does not fit rather than handing
1177+
* an out-of-range size to mailbox_dspbox_write().
1178+
*/
1179+
data_max = SOF_IPC_MSG_MAX_SIZE - (size_t)(data - (char *)ipc_get()->comp_data);
1180+
if (!ret && data_offset > data_max) {
1181+
ipc_cmd_err(&ipc_tr, "get_large_config reply size %u exceeds %zu",
1182+
data_offset, data_max);
1183+
ret = IPC4_INVALID_CONFIG_DATA_LEN;
1184+
}
1185+
11701186
/* Copy host config and overwrite */
11711187
reply.extension.dat = config->extension.dat;
11721188
reply.extension.r.data_off_size = data_offset;
@@ -1199,6 +1215,7 @@ __cold static int ipc4_get_large_config_module_instance(struct ipc4_message_requ
11991215
const struct comp_driver *drv;
12001216
struct comp_dev *dev = NULL;
12011217
uint32_t data_offset;
1218+
size_t data_max;
12021219
int ret;
12031220

12041221
assert_can_be_cold();
@@ -1274,6 +1291,21 @@ __cold static int ipc4_get_large_config_module_instance(struct ipc4_message_requ
12741291
if (ret < 0)
12751292
ret = IPC4_INVALID_RESOURCE_ID;
12761293

1294+
/* data_offset is now the reply payload size, but it was seeded from
1295+
* the host-controlled 20-bit data_off_size field and a module that
1296+
* produces no data leaves it untouched, so it can still claim up to
1297+
* ~1MB. The payload lives in ipc->comp_data (SOF_IPC_MSG_MAX_SIZE
1298+
* bytes) and ipc_platform_send_msg() copies tx_size bytes of it into
1299+
* the DSP mailbox, so reject anything that does not fit rather than
1300+
* handing an out-of-range size to mailbox_dspbox_write().
1301+
*/
1302+
data_max = SOF_IPC_MSG_MAX_SIZE - (size_t)(data - (char *)ipc_get()->comp_data);
1303+
if (!ret && data_offset > data_max) {
1304+
ipc_cmd_err(&ipc_tr, "get_large_config reply size %u exceeds %zu",
1305+
data_offset, data_max);
1306+
ret = IPC4_INVALID_CONFIG_DATA_LEN;
1307+
}
1308+
12771309
/* Copy host config and overwrite */
12781310
reply.extension.dat = config->extension.dat;
12791311
reply.extension.r.data_off_size = data_offset;

0 commit comments

Comments
 (0)