Skip to content

Commit cee34d8

Browse files
Peter ZijlstraIngo Molnar
authored andcommitted
lockdep: Fix a race between /proc/lock_stat and module unload
The lock_class iteration of /proc/lock_stat is not serialized against the lockdep_free_key_range() call from module unload. Therefore it can happen that we find a class of which ->name/->key are no longer valid. There is a further bug in zap_class() that left ->name dangling. Cure this. Use RCU_INIT_POINTER() because NULL. Since lockdep_free_key_range() is rcu_sched serialized, we can read both ->name and ->key under rcu_read_lock_sched() (preempt-disable) and be assured that if we observe a !NULL value it stays safe to use for as long as we hold that lock. If we observe both NULL, skip the entry. Reported-by: Jerome Marchand <[email protected]> Tested-by: Jerome Marchand <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Cc: Andrew Morton <[email protected]> Cc: H. Peter Anvin <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Thomas Gleixner <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent 37ef164 commit cee34d8

File tree

2 files changed

+19
-6
lines changed

2 files changed

+19
-6
lines changed

kernel/locking/lockdep.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3900,7 +3900,8 @@ static void zap_class(struct lock_class *class)
39003900
list_del_rcu(&class->hash_entry);
39013901
list_del_rcu(&class->lock_entry);
39023902

3903-
class->key = NULL;
3903+
RCU_INIT_POINTER(class->key, NULL);
3904+
RCU_INIT_POINTER(class->name, NULL);
39043905
}
39053906

39063907
static inline int within(const void *addr, void *start, unsigned long size)

kernel/locking/lockdep_proc.c

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -426,10 +426,12 @@ static void seq_lock_time(struct seq_file *m, struct lock_time *lt)
426426

427427
static void seq_stats(struct seq_file *m, struct lock_stat_data *data)
428428
{
429-
char name[39];
430-
struct lock_class *class;
429+
struct lockdep_subclass_key *ckey;
431430
struct lock_class_stats *stats;
431+
struct lock_class *class;
432+
const char *cname;
432433
int i, namelen;
434+
char name[39];
433435

434436
class = data->class;
435437
stats = &data->stats;
@@ -440,15 +442,25 @@ static void seq_stats(struct seq_file *m, struct lock_stat_data *data)
440442
if (class->subclass)
441443
namelen -= 2;
442444

443-
if (!class->name) {
445+
rcu_read_lock_sched();
446+
cname = rcu_dereference_sched(class->name);
447+
ckey = rcu_dereference_sched(class->key);
448+
449+
if (!cname && !ckey) {
450+
rcu_read_unlock_sched();
451+
return;
452+
453+
} else if (!cname) {
444454
char str[KSYM_NAME_LEN];
445455
const char *key_name;
446456

447-
key_name = __get_key_name(class->key, str);
457+
key_name = __get_key_name(ckey, str);
448458
snprintf(name, namelen, "%s", key_name);
449459
} else {
450-
snprintf(name, namelen, "%s", class->name);
460+
snprintf(name, namelen, "%s", cname);
451461
}
462+
rcu_read_unlock_sched();
463+
452464
namelen = strlen(name);
453465
if (class->name_version > 1) {
454466
snprintf(name+namelen, 3, "#%d", class->name_version);

0 commit comments

Comments
 (0)