Skip to content

Commit 00764aa

Browse files
gustavoldkuba-moo
authored andcommitted
netconsole: Fix race condition in between reader and writer of userdata
The update_userdata() function constructs the complete userdata string in nt->extradata_complete and updates nt->userdata_length. This data is then read by write_msg() and write_ext_msg() when sending netconsole messages. However, update_userdata() was not holding target_list_lock during this process, allowing concurrent message transmission to read partially updated userdata. This race condition could result in netconsole messages containing incomplete or inconsistent userdata - for example, reading the old userdata_length with new extradata_complete content, or vice versa, leading to truncated or corrupted output. Fix this by acquiring target_list_lock with spin_lock_irqsave() before updating extradata_complete and userdata_length, and releasing it after both fields are fully updated. This ensures that readers see a consistent view of the userdata, preventing corruption during concurrent access. The fix aligns with the existing locking pattern used throughout the netconsole code, where target_list_lock protects access to target fields including buf[] and msgcounter that are accessed during message transmission. Also get rid of the unnecessary variable complete_idx, which makes it easier to bail out of update_userdata(). Fixes: df03f83 ("net: netconsole: cache userdata formatted string in netconsole_target") Signed-off-by: Gustavo Luiz Duarte <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent a433038 commit 00764aa

1 file changed

Lines changed: 13 additions & 8 deletions

File tree

drivers/net/netconsole.c

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -886,8 +886,11 @@ static ssize_t userdatum_value_show(struct config_item *item, char *buf)
886886

887887
static void update_userdata(struct netconsole_target *nt)
888888
{
889-
int complete_idx = 0, child_count = 0;
890889
struct list_head *entry;
890+
int child_count = 0;
891+
unsigned long flags;
892+
893+
spin_lock_irqsave(&target_list_lock, flags);
891894

892895
/* Clear the current string in case the last userdatum was deleted */
893896
nt->userdata_length = 0;
@@ -897,8 +900,11 @@ static void update_userdata(struct netconsole_target *nt)
897900
struct userdatum *udm_item;
898901
struct config_item *item;
899902

900-
if (WARN_ON_ONCE(child_count >= MAX_EXTRADATA_ITEMS))
901-
break;
903+
if (child_count >= MAX_EXTRADATA_ITEMS) {
904+
spin_unlock_irqrestore(&target_list_lock, flags);
905+
WARN_ON_ONCE(1);
906+
return;
907+
}
902908
child_count++;
903909

904910
item = container_of(entry, struct config_item, ci_entry);
@@ -912,12 +918,11 @@ static void update_userdata(struct netconsole_target *nt)
912918
* one entry length (1/MAX_EXTRADATA_ITEMS long), entry count is
913919
* checked to not exceed MAX items with child_count above
914920
*/
915-
complete_idx += scnprintf(&nt->extradata_complete[complete_idx],
916-
MAX_EXTRADATA_ENTRY_LEN, " %s=%s\n",
917-
item->ci_name, udm_item->value);
921+
nt->userdata_length += scnprintf(&nt->extradata_complete[nt->userdata_length],
922+
MAX_EXTRADATA_ENTRY_LEN, " %s=%s\n",
923+
item->ci_name, udm_item->value);
918924
}
919-
nt->userdata_length = strnlen(nt->extradata_complete,
920-
sizeof(nt->extradata_complete));
925+
spin_unlock_irqrestore(&target_list_lock, flags);
921926
}
922927

923928
static ssize_t userdatum_value_store(struct config_item *item, const char *buf,

0 commit comments

Comments
 (0)