[PATCH] scsi: fnic: use scnprintf in fnic_get_debug_info()

From: Haoran Wang

Date: Mon Oct 19 2026 - 09:28:19 EST


fnic_get_debug_info() formats the fnic debugfs statistics buffer with
eight `len += snprintf(info->debug_buffer + len, buf_size - len, ...)`
calls into an 8192-byte vmalloc buffer (buf_size = 2*PAGE_SIZE).
snprintf returns the number of bytes that would have been written and
does not clamp on truncation, so once the buffer fills, `len` keeps
growing past buf_size.

The last call site is a loop over iport->tport_list with no
`len >= buf_size` guard.

list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
len += snprintf(info->debug_buffer + len, buf_size - len, ...);
}

tport entries are created one per GPN_FT target port with no upper
bound (fdls_create_tport). With a typical stats header (~4-6 KB) and
roughly 46 or more target ports, `len` crosses 8192. The next
iteration then computes `buf_size - len` as a negative int, which is
promoted to a huge size_t, and vsnprintf writes the full line past the
8192-byte vmalloc allocation into the guard page, causing a kernel
oops on the debugfs open() path. The inflated `return len` is also
stored as buffer_len and later drives an out-of-bounds read in
simple_read_from_buffer().

The sibling fnic_get_stats_data() in the same file already uses
scnprintf everywhere; fnic_get_debug_info() using snprintf is a porting
oversight.

Replace all eight snprintf calls with scnprintf. scnprintf returns
min(would-be, size-1), so `len` is bounded to buf_size-1 and
`buf_size - len` stays >= 1 (never negative).

Signed-off-by: Haoran Wang <haoranwangsec@xxxxxxxxx>
---
drivers/scsi/fnic/fnic_trace.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/scsi/fnic/fnic_trace.c b/drivers/scsi/fnic/fnic_trace.c
index 4ed57ea1f..51e7c973c 100644
--- a/drivers/scsi/fnic/fnic_trace.c
+++ b/drivers/scsi/fnic/fnic_trace.c
@@ -468,35 +468,35 @@ int fnic_get_debug_info(struct stats_debug_info *info, struct fnic *fnic)
struct fnic_tport_s *tport, *next;
unsigned long flags;

- len += snprintf(info->debug_buffer + len, buf_size - len,
+ len += scnprintf(info->debug_buffer + len, buf_size - len,
"------------------------------------------\n"
"\t\t Debug Info\n"
"------------------------------------------\n");
- len += snprintf(info->debug_buffer + len, buf_size - len,
+ len += scnprintf(info->debug_buffer + len, buf_size - len,
"fnic Name:%s number:%d Role:%s State:%s\n",
fnic->name, fnic->fnic_num,
fnic_role_to_str(fnic->role),
fnic_state_to_str(fnic->state));
len +=
- snprintf(info->debug_buffer + len, buf_size - len,
+ scnprintf(info->debug_buffer + len, buf_size - len,
"iport State:%d Flags:0x%x vlan_id:%d fcid:0x%x\n",
iport->state, iport->flags, iport->vlan_id, iport->fcid);
len +=
- snprintf(info->debug_buffer + len, buf_size - len,
+ scnprintf(info->debug_buffer + len, buf_size - len,
"usefip:%d fip_state:%d fip_flogi_retry:%d\n",
iport->usefip, iport->fip.state, iport->fip.flogi_retry);
len +=
- snprintf(info->debug_buffer + len, buf_size - len,
+ scnprintf(info->debug_buffer + len, buf_size - len,
"fpma %02x:%02x:%02x:%02x:%02x:%02x",
iport->fpma[5], iport->fpma[4], iport->fpma[3],
iport->fpma[2], iport->fpma[1], iport->fpma[0]);
len +=
- snprintf(info->debug_buffer + len, buf_size - len,
+ scnprintf(info->debug_buffer + len, buf_size - len,
"fcfmac %02x:%02x:%02x:%02x:%02x:%02x\n",
iport->fcfmac[5], iport->fcfmac[4], iport->fcfmac[3],
iport->fcfmac[2], iport->fcfmac[1], iport->fcfmac[0]);
len +=
- snprintf(info->debug_buffer + len, buf_size - len,
+ scnprintf(info->debug_buffer + len, buf_size - len,
"fabric state:%d flags:0x%x retry_counter:%d e_d_tov:%d r_a_tov:%d\n",
iport->fabric.state, iport->fabric.flags,
iport->fabric.retry_counter, iport->e_d_tov,
@@ -504,7 +504,7 @@ int fnic_get_debug_info(struct stats_debug_info *info, struct fnic *fnic)

spin_lock_irqsave(&fnic->fnic_lock, flags);
list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
- len += snprintf(info->debug_buffer + len, buf_size - len,
+ len += scnprintf(info->debug_buffer + len, buf_size - len,
"tport fcid:0x%x state:%d flags:0x%x inflight:%d retry_counter:%d\n",
tport->fcid, tport->state, tport->flags,
atomic_read(&tport->in_flight),