* [PATCH] fix a race between /proc/lock_stat and module unloading
@ 2015-05-29 12:47 Jerome Marchand
2015-06-02 9:30 ` Peter Zijlstra
0 siblings, 1 reply; 7+ messages in thread
From: Jerome Marchand @ 2015-05-29 12:47 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar; +Cc: linux-kernel
When opening /proc/lock_stat, lock_stat_open() makes a copy of
all_lock_classes list in the form of an array of ad hoc structures
lock_stat_data that reference lock_class, so it can be sorted and
passed to seq_read(). However, nothing prevents module unloading code
to free some of these lock_class structures before seq_read() tries to
access them.
Copying the all lock_class structures instead of just their pointers
would be an easy fix, but it seems quite wasteful. This patch copies
only the needed data into the lock_stat_data structure.
Signed-off-by: Jerome Marchand <jmarchan@redhat.com>
---
kernel/locking/lockdep_proc.c | 88 ++++++++++++++++++++++++-------------------
1 file changed, 50 insertions(+), 38 deletions(-)
diff --git a/kernel/locking/lockdep_proc.c b/kernel/locking/lockdep_proc.c
index ef43ac4..c2eb8e8 100644
--- a/kernel/locking/lockdep_proc.c
+++ b/kernel/locking/lockdep_proc.c
@@ -363,7 +363,9 @@ static const struct file_operations proc_lockdep_stats_operations = {
#ifdef CONFIG_LOCK_STAT
struct lock_stat_data {
- struct lock_class *class;
+ char name[39];
+ unsigned long contention_point[LOCKSTAT_POINTS];
+ unsigned long contending_point[LOCKSTAT_POINTS];
struct lock_class_stats stats;
};
@@ -426,39 +428,12 @@ static void seq_lock_time(struct seq_file *m, struct lock_time *lt)
static void seq_stats(struct seq_file *m, struct lock_stat_data *data)
{
- char name[39];
- struct lock_class *class;
+ char *name = data->name;
struct lock_class_stats *stats;
- int i, namelen;
+ int i, namelen = strlen(data->name);
- class = data->class;
stats = &data->stats;
- namelen = 38;
- if (class->name_version > 1)
- namelen -= 2; /* XXX truncates versions > 9 */
- if (class->subclass)
- namelen -= 2;
-
- if (!class->name) {
- char str[KSYM_NAME_LEN];
- const char *key_name;
-
- key_name = __get_key_name(class->key, str);
- snprintf(name, namelen, "%s", key_name);
- } else {
- snprintf(name, namelen, "%s", class->name);
- }
- namelen = strlen(name);
- if (class->name_version > 1) {
- snprintf(name+namelen, 3, "#%d", class->name_version);
- namelen += 2;
- }
- if (class->subclass) {
- snprintf(name+namelen, 3, "/%d", class->subclass);
- namelen += 2;
- }
-
if (stats->write_holdtime.nr) {
if (stats->read_holdtime.nr)
seq_printf(m, "%38s-W:", name);
@@ -490,32 +465,32 @@ static void seq_stats(struct seq_file *m, struct lock_stat_data *data)
for (i = 0; i < LOCKSTAT_POINTS; i++) {
char ip[32];
- if (class->contention_point[i] == 0)
+ if (data->contention_point[i] == 0)
break;
if (!i)
seq_line(m, '-', 40-namelen, namelen);
snprintf(ip, sizeof(ip), "[<%p>]",
- (void *)class->contention_point[i]);
+ (void *)data->contention_point[i]);
seq_printf(m, "%40s %14lu %29s %pS\n",
name, stats->contention_point[i],
- ip, (void *)class->contention_point[i]);
+ ip, (void *)data->contention_point[i]);
}
for (i = 0; i < LOCKSTAT_POINTS; i++) {
char ip[32];
- if (class->contending_point[i] == 0)
+ if (data->contending_point[i] == 0)
break;
if (!i)
seq_line(m, '-', 40-namelen, namelen);
snprintf(ip, sizeof(ip), "[<%p>]",
- (void *)class->contending_point[i]);
+ (void *)data->contending_point[i]);
seq_printf(m, "%40s %14lu %29s %pS\n",
name, stats->contending_point[i],
- ip, (void *)class->contending_point[i]);
+ ip, (void *)data->contending_point[i]);
}
if (i) {
seq_puts(m, "\n");
@@ -593,6 +568,44 @@ static const struct seq_operations lockstat_ops = {
.show = ls_show,
};
+static void copy_lock_class(struct lock_stat_data *data,
+ struct lock_class *class)
+{
+ char *name = data->name;
+ int namelen = 38;
+
+ if (class->name_version > 1)
+ namelen -= 2; /* XXX truncates versions > 9 */
+ if (class->subclass)
+ namelen -= 2;
+
+ if (!class->name) {
+ char str[KSYM_NAME_LEN];
+ const char *key_name;
+
+ key_name = __get_key_name(class->key, str);
+ snprintf(name, namelen, "%s", key_name);
+ } else {
+ snprintf(name, namelen, "%s", class->name);
+ }
+ namelen = strlen(name);
+ if (class->name_version > 1) {
+ snprintf(name+namelen, 3, "#%d", class->name_version);
+ namelen += 2;
+ }
+ if (class->subclass) {
+ snprintf(name+namelen, 3, "/%d", class->subclass);
+ namelen += 2;
+ }
+
+ memcpy(data->contention_point, class->contention_point,
+ sizeof(class->contention_point));
+ memcpy(data->contending_point, class->contending_point,
+ sizeof(class->contending_point));
+
+ data->stats = lock_stats(class);
+}
+
static int lock_stat_open(struct inode *inode, struct file *file)
{
int res;
@@ -608,8 +621,7 @@ static int lock_stat_open(struct inode *inode, struct file *file)
struct seq_file *m = file->private_data;
list_for_each_entry(class, &all_lock_classes, lock_entry) {
- iter->class = class;
- iter->stats = lock_stats(class);
+ copy_lock_class(iter, class);
iter++;
}
data->iter_end = iter;
--
1.9.3
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH] fix a race between /proc/lock_stat and module unloading
2015-05-29 12:47 [PATCH] fix a race between /proc/lock_stat and module unloading Jerome Marchand
@ 2015-06-02 9:30 ` Peter Zijlstra
2015-06-02 9:54 ` Jerome Marchand
0 siblings, 1 reply; 7+ messages in thread
From: Peter Zijlstra @ 2015-06-02 9:30 UTC (permalink / raw)
To: Jerome Marchand; +Cc: Ingo Molnar, linux-kernel
On Fri, May 29, 2015 at 02:47:15PM +0200, Jerome Marchand wrote:
> When opening /proc/lock_stat, lock_stat_open() makes a copy of
> all_lock_classes list in the form of an array of ad hoc structures
> lock_stat_data that reference lock_class, so it can be sorted and
> passed to seq_read(). However, nothing prevents module unloading code
> to free some of these lock_class structures before seq_read() tries to
> access them.
Well, how about lock_class being from a static array in lockdep.c:138 ?
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] fix a race between /proc/lock_stat and module unloading
2015-06-02 9:30 ` Peter Zijlstra
@ 2015-06-02 9:54 ` Jerome Marchand
2015-06-02 10:50 ` Peter Zijlstra
0 siblings, 1 reply; 7+ messages in thread
From: Jerome Marchand @ 2015-06-02 9:54 UTC (permalink / raw)
To: Peter Zijlstra; +Cc: Ingo Molnar, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 5066 bytes --]
On 06/02/2015 11:30 AM, Peter Zijlstra wrote:
> On Fri, May 29, 2015 at 02:47:15PM +0200, Jerome Marchand wrote:
>> When opening /proc/lock_stat, lock_stat_open() makes a copy of
>> all_lock_classes list in the form of an array of ad hoc structures
>> lock_stat_data that reference lock_class, so it can be sorted and
>> passed to seq_read(). However, nothing prevents module unloading code
>> to free some of these lock_class structures before seq_read() tries to
>> access them.
>
> Well, how about lock_class being from a static array in lockdep.c:138 ?
>
>
I guess I jumped to conclusion here and my explanation is wrong. However
there is still a bug which occurs when the kernel tries to access
class->name is seq_stats:
[ 43.533732] BUG: unable to handle kernel paging request at
ffffffffa03181ce
[ 43.534006] IP: [<ffffffff8142b489>] strnlen+0x9/0x50
[ 43.534006] PGD 1e14067 PUD 1e15063 PMD 79153067 PTE 0
[ 43.534006] Oops: 0000 [#1] SMP
[ 43.534006] Modules linked in: ip6t_rpfilter ip6t_REJECT
nf_reject_ipv6 xt_conntrack ebtable_nat ebtable_broute bridge stp llc
ebtable_filter ebtables ip6table_nat nf_conntrack_ipv6 nf_defrag_ipv6
nf_nat_ipv6 ip6table_mangle ip6table_security ip6table_raw
ip6table_filter ip6_tables iptable_nat nf_conntrack_ipv4 nf_defrag_ipv4
nf_nat_ipv4 nf_nat nf_conntrack iptable_mangle iptable_security
iptable_raw ppdev iosf_mbi crct10dif_pclmul crc32_pclmul crc32c_intel
ghash_clmulni_intel serio_raw virtio_balloon virtio_console parport_pc
parport pvpanic i2c_piix4 nfsd auth_rpcgss nfs_acl lockd grace sunrpc
virtio_blk virtio_net qxl drm_kms_helper ttm drm virtio_pci virtio_ring
virtio ata_generic pata_acpi [last unloaded: zram]
[ 43.534006] CPU: 0 PID: 2125 Comm: cat Not tainted
3.19.4-200.fc21.x86_64+debug #1
[ 43.534006] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
[ 43.534006] task: ffff88007a468000 ti: ffff88007a374000 task.ti:
ffff88007a374000
[ 43.534006] RIP: 0010:[<ffffffff8142b489>] [<ffffffff8142b489>]
strnlen+0x9/0x50
[ 43.534006] RSP: 0018:ffff88007a377ba8 EFLAGS: 00010286
[ 43.534006] RAX: ffffffff81c7ac25 RBX: ffff88007a377ce9 RCX:
0000000000000000
[ 43.534006] RDX: ffffffffa03181ce RSI: ffffffffffffffff RDI:
ffffffffa03181ce
[ 43.534006] RBP: ffff88007a377ba8 R08: 000000000000ffff R09:
000000000000ffff
[ 43.534006] R10: 0000000000000000 R11: 0000000000000000 R12:
ffffffffa03181ce
[ 43.534006] R13: ffff88007a377d0f R14: 00000000ffffffff R15:
0000000000000000
[ 43.534006] FS: 00007f6e132f3700(0000) GS:ffff88007d200000(0000)
knlGS:0000000000000000
[ 43.534006] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 43.534006] CR2: ffffffffa03181ce CR3: 000000007b777000 CR4:
00000000001406f0
[ 43.534006] DR0: 0000000000000000 DR1: 0000000000000000 DR2:
0000000000000000
[ 43.534006] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7:
0000000000000400
[ 43.534006] Stack:
[ 43.534006] ffff88007a377be8 ffffffff8142d92f ffff88007a377c81
ffff88007a377ce9
[ 43.534006] ffff88007a377d0f ffff88007a377c78 ffffffff81cd0158
ffffffff81cd0158
[ 43.534006] ffff88007a377c68 ffffffff8142f0d9 ffff88007a377ce9
ffff88007b128800
[ 43.534006] Call Trace:
[ 43.534006] [<ffffffff8142d92f>] string.isra.7+0x3f/0xf0
[ 43.534006] [<ffffffff8142f0d9>] vsnprintf+0x199/0x5b0
[ 43.534006] [<ffffffff812a329c>] ? seq_printf+0x4c/0x70
[ 43.534006] [<ffffffff8142f593>] snprintf+0x43/0x60
[ 43.534006] [<ffffffff812a33c8>] ? seq_puts+0x48/0x70
[ 43.534006] [<ffffffff8111278c>] seq_stats+0x7c/0x520
[ 43.534006] [<ffffffff8110db4c>] ? mark_held_locks+0x7c/0xb0
[ 43.534006] [<ffffffff8187486c>] ? mutex_lock_nested+0x28c/0x440
[ 43.534006] [<ffffffff8110dcbd>] ? trace_hardirqs_on_caller+0x13d/0x1e0
[ 43.534006] [<ffffffff81112c47>] ls_show+0x17/0x120
[ 43.534006] [<ffffffff812c1822>] ? fsnotify+0x462/0x820
[ 43.534006] [<ffffffff812c1458>] ? fsnotify+0x98/0x820
[ 43.534006] [<ffffffff812a2cf6>] seq_read+0x316/0x400
[ 43.534006] [<ffffffff812f06a8>] proc_reg_read+0x48/0x70
[ 43.534006] [<ffffffff81277378>] __vfs_read+0x18/0x50
[ 43.534006] [<ffffffff8127743d>] vfs_read+0x8d/0x150
[ 43.534006] [<ffffffff8127755c>] SyS_read+0x5c/0xd0
[ 43.534006] [<ffffffff81879389>] system_call_fastpath+0x12/0x17
[ 43.534006] Code: 40 00 48 83 c0 01 80 38 00 75 f7 48 29 f8 5d c3 31
c0 5d c3 66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00 55 48 85 f6 48 89 e5
74 3f <80> 3f 00 74 3a 48 8d 47 01 48 01 fe eb 13 66 0f 1f 84 00 00 00
[ 43.534006] RIP [<ffffffff8142b489>] strnlen+0x9/0x50
[ 43.534006] RSP <ffff88007a377ba8>
[ 43.534006] CR2: ffffffffa03181ce
[ 43.534006] ---[ end trace 609a4a4bd210562d ]---
So I guess it's actually just class->name that get freed underneath us.
The following script easily triggers the bug unless my patch is applied:
#! /bin/sh
while true; do
modprobe zram;
modprobe -r zram;
done &
while true; do
cat /proc/lock_stat > /dev/null ;
done
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH] fix a race between /proc/lock_stat and module unloading
2015-06-02 9:54 ` Jerome Marchand
@ 2015-06-02 10:50 ` Peter Zijlstra
2015-06-02 14:15 ` Jerome Marchand
2015-06-07 17:45 ` [tip:locking/urgent] lockdep: Fix a race between /proc/ lock_stat and module unload tip-bot for Peter Zijlstra
0 siblings, 2 replies; 7+ messages in thread
From: Peter Zijlstra @ 2015-06-02 10:50 UTC (permalink / raw)
To: Jerome Marchand; +Cc: Ingo Molnar, linux-kernel
On Tue, Jun 02, 2015 at 11:54:13AM +0200, Jerome Marchand wrote:
>
> I guess I jumped to conclusion here and my explanation is wrong. However
> there is still a bug which occurs when the kernel tries to access
> class->name is seq_stats:
>
> [ 43.533732] BUG: unable to handle kernel paging request at
> ffffffffa03181ce
> [ 43.534006] IP: [<ffffffff8142b489>] strnlen+0x9/0x50
Does something like so cure things?
---
kernel/locking/lockdep.c | 3 ++-
kernel/locking/lockdep_proc.c | 22 +++++++++++++++++-----
2 files changed, 19 insertions(+), 6 deletions(-)
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index a0831e1b99f4..50ed2685f937 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -3900,7 +3900,8 @@ static void zap_class(struct lock_class *class)
list_del_rcu(&class->hash_entry);
list_del_rcu(&class->lock_entry);
- class->key = NULL;
+ WRITE_ONCE(class->key, NULL);
+ WRITE_ONCE(class->name, NULL);
}
static inline int within(const void *addr, void *start, unsigned long size)
diff --git a/kernel/locking/lockdep_proc.c b/kernel/locking/lockdep_proc.c
index ef43ac4bafb5..03946779eacc 100644
--- a/kernel/locking/lockdep_proc.c
+++ b/kernel/locking/lockdep_proc.c
@@ -426,10 +426,12 @@ static void seq_lock_time(struct seq_file *m, struct lock_time *lt)
static void seq_stats(struct seq_file *m, struct lock_stat_data *data)
{
- char name[39];
- struct lock_class *class;
+ struct lockdep_subclass_key *ckey;
struct lock_class_stats *stats;
+ struct lock_class *class;
int i, namelen;
+ char name[39];
+ char *cname;
class = data->class;
stats = &data->stats;
@@ -440,15 +442,25 @@ static void seq_stats(struct seq_file *m, struct lock_stat_data *data)
if (class->subclass)
namelen -= 2;
- if (!class->name) {
+ rcu_read_lock_sched();
+ cname = rcu_dereference_sched(class->name);
+ ckey = rcu_dereference_sched(class->key);
+
+ if (!cname && !ckey) {
+ rcu_read_unlock_sched();
+ return;
+
+ } else if (!cname) {
char str[KSYM_NAME_LEN];
const char *key_name;
- key_name = __get_key_name(class->key, str);
+ key_name = __get_key_name(ckey, str);
snprintf(name, namelen, "%s", key_name);
} else {
- snprintf(name, namelen, "%s", class->name);
+ snprintf(name, namelen, "%s", cname);
}
+ rcu_read_unlock_sched();
+
namelen = strlen(name);
if (class->name_version > 1) {
snprintf(name+namelen, 3, "#%d", class->name_version);
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH] fix a race between /proc/lock_stat and module unloading
2015-06-02 10:50 ` Peter Zijlstra
@ 2015-06-02 14:15 ` Jerome Marchand
2015-06-02 15:01 ` Peter Zijlstra
2015-06-07 17:45 ` [tip:locking/urgent] lockdep: Fix a race between /proc/ lock_stat and module unload tip-bot for Peter Zijlstra
1 sibling, 1 reply; 7+ messages in thread
From: Jerome Marchand @ 2015-06-02 14:15 UTC (permalink / raw)
To: Peter Zijlstra; +Cc: Ingo Molnar, linux-kernel
[-- Attachment #1: Type: text/plain, Size: 2782 bytes --]
On 06/02/2015 12:50 PM, Peter Zijlstra wrote:
> On Tue, Jun 02, 2015 at 11:54:13AM +0200, Jerome Marchand wrote:
>>
>> I guess I jumped to conclusion here and my explanation is wrong. However
>> there is still a bug which occurs when the kernel tries to access
>> class->name is seq_stats:
>>
>> [ 43.533732] BUG: unable to handle kernel paging request at
>> ffffffffa03181ce
>> [ 43.534006] IP: [<ffffffff8142b489>] strnlen+0x9/0x50
>
> Does something like so cure things?
Yes, I can't reproduce the issue anymore.
>
> ---
> kernel/locking/lockdep.c | 3 ++-
> kernel/locking/lockdep_proc.c | 22 +++++++++++++++++-----
> 2 files changed, 19 insertions(+), 6 deletions(-)
>
> diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
> index a0831e1b99f4..50ed2685f937 100644
> --- a/kernel/locking/lockdep.c
> +++ b/kernel/locking/lockdep.c
> @@ -3900,7 +3900,8 @@ static void zap_class(struct lock_class *class)
> list_del_rcu(&class->hash_entry);
> list_del_rcu(&class->lock_entry);
>
> - class->key = NULL;
> + WRITE_ONCE(class->key, NULL);
> + WRITE_ONCE(class->name, NULL);
> }
>
> static inline int within(const void *addr, void *start, unsigned long size)
> diff --git a/kernel/locking/lockdep_proc.c b/kernel/locking/lockdep_proc.c
> index ef43ac4bafb5..03946779eacc 100644
> --- a/kernel/locking/lockdep_proc.c
> +++ b/kernel/locking/lockdep_proc.c
> @@ -426,10 +426,12 @@ static void seq_lock_time(struct seq_file *m, struct lock_time *lt)
>
> static void seq_stats(struct seq_file *m, struct lock_stat_data *data)
> {
> - char name[39];
> - struct lock_class *class;
> + struct lockdep_subclass_key *ckey;
> struct lock_class_stats *stats;
> + struct lock_class *class;
> int i, namelen;
> + char name[39];
> + char *cname;
>
> class = data->class;
> stats = &data->stats;
> @@ -440,15 +442,25 @@ static void seq_stats(struct seq_file *m, struct lock_stat_data *data)
> if (class->subclass)
> namelen -= 2;
>
> - if (!class->name) {
> + rcu_read_lock_sched();
> + cname = rcu_dereference_sched(class->name);
> + ckey = rcu_dereference_sched(class->key);
> +
> + if (!cname && !ckey) {
> + rcu_read_unlock_sched();
> + return;
> +
> + } else if (!cname) {
> char str[KSYM_NAME_LEN];
> const char *key_name;
>
> - key_name = __get_key_name(class->key, str);
> + key_name = __get_key_name(ckey, str);
> snprintf(name, namelen, "%s", key_name);
> } else {
> - snprintf(name, namelen, "%s", class->name);
> + snprintf(name, namelen, "%s", cname);
> }
> + rcu_read_unlock_sched();
> +
> namelen = strlen(name);
> if (class->name_version > 1) {
> snprintf(name+namelen, 3, "#%d", class->name_version);
>
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH] fix a race between /proc/lock_stat and module unloading
2015-06-02 14:15 ` Jerome Marchand
@ 2015-06-02 15:01 ` Peter Zijlstra
0 siblings, 0 replies; 7+ messages in thread
From: Peter Zijlstra @ 2015-06-02 15:01 UTC (permalink / raw)
To: Jerome Marchand; +Cc: Ingo Molnar, linux-kernel
On Tue, Jun 02, 2015 at 04:15:24PM +0200, Jerome Marchand wrote:
> Yes, I can't reproduce the issue anymore.
Great; queued the below. Slight changes
- s/WRITE_ONCE/RCU_INIT_POINTER/ which should be similar but more descriptive
- const char *cname to avoid a compile warn.
---
Subject: lockdep: Fix a race between /proc/lock_stat and module unload
From: Peter Zijlstra <peterz@infradead.org>
Date: Tue, 2 Jun 2015 12:50:13 +0200
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.
Cc: Ingo Molnar <mingo@redhat.com>
Reported-by: Jerome Marchand <jmarchan@redhat.com>
Tested-by: Jerome Marchand <jmarchan@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20150602105013.GS3644@twins.programming.kicks-ass.net
---
kernel/locking/lockdep.c | 3 ++-
kernel/locking/lockdep_proc.c | 22 +++++++++++++++++-----
2 files changed, 19 insertions(+), 6 deletions(-)
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -3900,7 +3900,8 @@ static void zap_class(struct lock_class
list_del_rcu(&class->hash_entry);
list_del_rcu(&class->lock_entry);
- class->key = NULL;
+ RCU_INIT_POINTER(class->key, NULL);
+ RCU_INIT_POINTER(class->name, NULL);
}
static inline int within(const void *addr, void *start, unsigned long size)
--- a/kernel/locking/lockdep_proc.c
+++ b/kernel/locking/lockdep_proc.c
@@ -426,10 +426,12 @@ static void seq_lock_time(struct seq_fil
static void seq_stats(struct seq_file *m, struct lock_stat_data *data)
{
- char name[39];
- struct lock_class *class;
+ struct lockdep_subclass_key *ckey;
struct lock_class_stats *stats;
+ struct lock_class *class;
+ const char *cname;
int i, namelen;
+ char name[39];
class = data->class;
stats = &data->stats;
@@ -440,15 +442,25 @@ static void seq_stats(struct seq_file *m
if (class->subclass)
namelen -= 2;
- if (!class->name) {
+ rcu_read_lock_sched();
+ cname = rcu_dereference_sched(class->name);
+ ckey = rcu_dereference_sched(class->key);
+
+ if (!cname && !ckey) {
+ rcu_read_unlock_sched();
+ return;
+
+ } else if (!cname) {
char str[KSYM_NAME_LEN];
const char *key_name;
- key_name = __get_key_name(class->key, str);
+ key_name = __get_key_name(ckey, str);
snprintf(name, namelen, "%s", key_name);
} else {
- snprintf(name, namelen, "%s", class->name);
+ snprintf(name, namelen, "%s", cname);
}
+ rcu_read_unlock_sched();
+
namelen = strlen(name);
if (class->name_version > 1) {
snprintf(name+namelen, 3, "#%d", class->name_version);
^ permalink raw reply [flat|nested] 7+ messages in thread
* [tip:locking/urgent] lockdep: Fix a race between /proc/ lock_stat and module unload
2015-06-02 10:50 ` Peter Zijlstra
2015-06-02 14:15 ` Jerome Marchand
@ 2015-06-07 17:45 ` tip-bot for Peter Zijlstra
1 sibling, 0 replies; 7+ messages in thread
From: tip-bot for Peter Zijlstra @ 2015-06-07 17:45 UTC (permalink / raw)
To: linux-tip-commits
Cc: akpm, mingo, torvalds, peterz, linux-kernel, jmarchan, hpa, tglx
Commit-ID: cee34d88cabd1ba5fc93e09b5b12232bc9338c7c
Gitweb: http://git.kernel.org/tip/cee34d88cabd1ba5fc93e09b5b12232bc9338c7c
Author: Peter Zijlstra <peterz@infradead.org>
AuthorDate: Tue, 2 Jun 2015 12:50:13 +0200
Committer: Ingo Molnar <mingo@kernel.org>
CommitDate: Sun, 7 Jun 2015 15:46:30 +0200
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 <jmarchan@redhat.com>
Tested-by: Jerome Marchand <jmarchan@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/20150602105013.GS3644@twins.programming.kicks-ass.net
Signed-off-by: Ingo Molnar <mingo@kernel.org>
---
kernel/locking/lockdep.c | 3 ++-
kernel/locking/lockdep_proc.c | 22 +++++++++++++++++-----
2 files changed, 19 insertions(+), 6 deletions(-)
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index a0831e1..aaeae88 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -3900,7 +3900,8 @@ static void zap_class(struct lock_class *class)
list_del_rcu(&class->hash_entry);
list_del_rcu(&class->lock_entry);
- class->key = NULL;
+ RCU_INIT_POINTER(class->key, NULL);
+ RCU_INIT_POINTER(class->name, NULL);
}
static inline int within(const void *addr, void *start, unsigned long size)
diff --git a/kernel/locking/lockdep_proc.c b/kernel/locking/lockdep_proc.c
index ef43ac4..d83d798 100644
--- a/kernel/locking/lockdep_proc.c
+++ b/kernel/locking/lockdep_proc.c
@@ -426,10 +426,12 @@ static void seq_lock_time(struct seq_file *m, struct lock_time *lt)
static void seq_stats(struct seq_file *m, struct lock_stat_data *data)
{
- char name[39];
- struct lock_class *class;
+ struct lockdep_subclass_key *ckey;
struct lock_class_stats *stats;
+ struct lock_class *class;
+ const char *cname;
int i, namelen;
+ char name[39];
class = data->class;
stats = &data->stats;
@@ -440,15 +442,25 @@ static void seq_stats(struct seq_file *m, struct lock_stat_data *data)
if (class->subclass)
namelen -= 2;
- if (!class->name) {
+ rcu_read_lock_sched();
+ cname = rcu_dereference_sched(class->name);
+ ckey = rcu_dereference_sched(class->key);
+
+ if (!cname && !ckey) {
+ rcu_read_unlock_sched();
+ return;
+
+ } else if (!cname) {
char str[KSYM_NAME_LEN];
const char *key_name;
- key_name = __get_key_name(class->key, str);
+ key_name = __get_key_name(ckey, str);
snprintf(name, namelen, "%s", key_name);
} else {
- snprintf(name, namelen, "%s", class->name);
+ snprintf(name, namelen, "%s", cname);
}
+ rcu_read_unlock_sched();
+
namelen = strlen(name);
if (class->name_version > 1) {
snprintf(name+namelen, 3, "#%d", class->name_version);
^ permalink raw reply related [flat|nested] 7+ messages in thread
end of thread, other threads:[~2015-06-07 17:46 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-05-29 12:47 [PATCH] fix a race between /proc/lock_stat and module unloading Jerome Marchand
2015-06-02 9:30 ` Peter Zijlstra
2015-06-02 9:54 ` Jerome Marchand
2015-06-02 10:50 ` Peter Zijlstra
2015-06-02 14:15 ` Jerome Marchand
2015-06-02 15:01 ` Peter Zijlstra
2015-06-07 17:45 ` [tip:locking/urgent] lockdep: Fix a race between /proc/ lock_stat and module unload tip-bot for Peter Zijlstra
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.