Skip to contentSkip to conversation

26c4caea "don't allow duplicate entries in listener mode" fix

PATCH In review Oleg Nesterov 4 participants 10 messages Jul 20, 2011 – Jul 21, 2011 Taskstats statistics interface

lore
Share & tools
lore
#0 lore raw

cover 26c4caea "don't allow duplicate entries in listener mode" fix

Hello.

I noticed 26c4caea by accident. Afaics it deserves a couple of minor fixes/cleanups.

COMPILE TESTED ONLY. Needs review and acks ;)

Can't we kill listener->valid? It seems we can set ->pid = 0 to mark it invalid.

Oleg.

#1 lore raw

PATCH 1/2 taskstats: add_del_listener() shouldn't use the wrong node

1. 26c4caea "don't allow duplicate entries in listener mode" changed add_del_listener(REGISTER) so that "next_cpu:" can reuse the listener allocated for the previous cpu, this doesn't look exactly right even if minor.

Change the code to kfree() in the already-registered case, this case is unlikely anyway so the extra kmalloc_node() shouldn't hurt but looke more correct and clean.

2. use the plain list_for_each_entry() instead of _safe() to scan listeners->list.

3. Remove the unneeded INIT_LIST_HEAD(&s->list), we are going to list_add(&s->list).

---

 kernel/taskstats.c |   16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)
kernel/taskstats.c renamed from kernel/taskstats.c~1_cleanup +7 -9
@@ -291,30 +291,28 @@ static int add_del_listener(pid_t pid, c
 	if (!cpumask_subset(mask, cpu_possible_mask))
 		return -EINVAL;
 
-	s = NULL;
 	if (isadd == REGISTER) {
 		for_each_cpu(cpu, mask) {
-			if (!s)
-				s = kmalloc_node(sizeof(struct listener),
-						 GFP_KERNEL, cpu_to_node(cpu));
+			s = kmalloc_node(sizeof(struct listener),
+					GFP_KERNEL, cpu_to_node(cpu));
 			if (!s)
 				goto cleanup;
+
 			s->pid = pid;
-			INIT_LIST_HEAD(&s->list);
 			s->valid = 1;
 
 			listeners = &per_cpu(listener_array, cpu);
 			down_write(&listeners->sem);
-			list_for_each_entry_safe(s2, tmp, &listeners->list, list) {
+			list_for_each_entry(s2, &listeners->list, list) {
 				if (s2->pid == pid)
-					goto next_cpu;
+					goto exists;
 			}
 			list_add(&s->list, &listeners->list);
 			s = NULL;
-next_cpu:
+exists:
 			up_write(&listeners->sem);
+			kfree(s); /* nop if NULL */
 		}
-		kfree(s);
 		return 0;
 	}
 
Reviewed-by on v1 1/2
#2 lore raw

Re: [PATCH 1/2] taskstats: add_del_listener() shouldn't use the wrong node

Quoted 9 lines · Oleg Nesterov

1. 26c4caea "don't allow duplicate entries in listener mode" changed add_del_listener(REGISTER) so that "next_cpu:" can reuse the listener allocated for the previous cpu, this doesn't look exactly right even if minor.

Change the code to kfree() in the already-registered case, this case is unlikely anyway so the extra kmalloc_node() shouldn't hurt but looke more correct and clean.

Quoted 5 lines

2. use the plain list_for_each_entry() instead of _safe() to scan listeners->list.

3. Remove the unneeded INIT_LIST_HEAD(&s->list), we are going to list_add(&s->list).

I don't know whether (1) is needed, looks very minor to me. Other than that:

Quoted 1 line
Quoted 46 lines

---

 kernel/taskstats.c |   16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)
kernel/taskstats.c renamed from kernel/taskstats.c~1_cleanup +7 -9
@@ -291,30 +291,28 @@ static int add_del_listener(pid_t pid, c
 	if (!cpumask_subset(mask, cpu_possible_mask))
 		return -EINVAL;
 
-	s = NULL;
 	if (isadd == REGISTER) {
 		for_each_cpu(cpu, mask) {
-			if (!s)
-				s = kmalloc_node(sizeof(struct listener),
-						 GFP_KERNEL, cpu_to_node(cpu));
+			s = kmalloc_node(sizeof(struct listener),
+					GFP_KERNEL, cpu_to_node(cpu));
 			if (!s)
 				goto cleanup;
+
 			s->pid = pid;
-			INIT_LIST_HEAD(&s->list);
 			s->valid = 1;
 
 			listeners = &per_cpu(listener_array, cpu);
 			down_write(&listeners->sem);
-			list_for_each_entry_safe(s2, tmp, &listeners->list, list) {
+			list_for_each_entry(s2, &listeners->list, list) {
 				if (s2->pid == pid)
-					goto next_cpu;
+					goto exists;
 			}
 			list_add(&s->list, &listeners->list);
 			s = NULL;
-next_cpu:
+exists:
 			up_write(&listeners->sem);
+			kfree(s); /* nop if NULL */
 		}
-		kfree(s);
 		return 0;
 	}
 
Signature · 2 lines
Vasiliy Kulikov
http://www.openwall.com - bringing security into open computing environments
Reviewed-by on v1 1/2
#3 lore raw

Re: [PATCH 1/2] taskstats: add_del_listener() shouldn't use the wrong node

In reply to Oleg Nesterov
Quoted 4 lines · Oleg Nesterov

1. 26c4caea "don't allow duplicate entries in listener mode" changed add_del_listener(REGISTER) so that "next_cpu:" can reuse the listener allocated for the previous cpu, this doesn't look exactly right even if minor.

That construction is somewhat unusual. I agree that this code looks cleaner and probably reduces the risk that someone in the future misunderstand the code.

Quoted 5 lines

Change the code to kfree() in the already-registered case, this case is unlikely anyway so the extra kmalloc_node() shouldn't hurt but looke more correct and clean.

Quoted 7 lines

2. use the plain list_for_each_entry() instead of _safe() to scan listeners->list.

3. Remove the unneeded INIT_LIST_HEAD(&s->list), we are going to list_add(&s->list).

Quoted 46 lines

---

 kernel/taskstats.c |   16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)
kernel/taskstats.c renamed from kernel/taskstats.c~1_cleanup +7 -9
@@ -291,30 +291,28 @@ static int add_del_listener(pid_t pid, c
 	if (!cpumask_subset(mask, cpu_possible_mask))
 		return -EINVAL;
 
-	s = NULL;
 	if (isadd == REGISTER) {
 		for_each_cpu(cpu, mask) {
-			if (!s)
-				s = kmalloc_node(sizeof(struct listener),
-						 GFP_KERNEL, cpu_to_node(cpu));
+			s = kmalloc_node(sizeof(struct listener),
+					GFP_KERNEL, cpu_to_node(cpu));
 			if (!s)
 				goto cleanup;
+
 			s->pid = pid;
-			INIT_LIST_HEAD(&s->list);
 			s->valid = 1;
 
 			listeners = &per_cpu(listener_array, cpu);
 			down_write(&listeners->sem);
-			list_for_each_entry_safe(s2, tmp, &listeners->list, list) {
+			list_for_each_entry(s2, &listeners->list, list) {
 				if (s2->pid == pid)
-					goto next_cpu;
+					goto exists;
 			}
 			list_add(&s->list, &listeners->list);
 			s = NULL;
-next_cpu:
+exists:
 			up_write(&listeners->sem);
+			kfree(s); /* nop if NULL */
 		}
-		kfree(s);
 		return 0;
 	}
 
#4 lore raw

PATCH 2/2 taskstats: add_del_listener() should ignore !valid listener's

In reply to Oleg Nesterov

When send_cpu_listeners() finds the orphaned listener it marks it as !valid and drops listeners->sem. Before it takes this sem for wrinting, s->pid can be reused and add_del_listener() can wrongly try to re-use this entry.

Change add_del_listener() to check ->valid = T.

---

 kernel/taskstats.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/taskstats.c renamed from kernel/taskstats.c~2_ck_valid +1 -1
@@ -304,7 +304,7 @@ static int add_del_listener(pid_t pid, c
 			listeners = &per_cpu(listener_array, cpu);
 			down_write(&listeners->sem);
 			list_for_each_entry(s2, &listeners->list, list) {
-				if (s2->pid == pid)
+				if (s2->pid == pid && s2->valid)
 					goto exists;
 			}
 			list_add(&s->list, &listeners->list);
#5 lore raw

Re: [PATCH 2/2] taskstats: add_del_listener() should ignore !valid listener's

Quoted 6 lines · Oleg Nesterov

When send_cpu_listeners() finds the orphaned listener it marks it as !valid and drops listeners->sem. Before it takes this sem for wrinting, s->pid can be reused and add_del_listener() can wrongly try to re-use this entry.

Change add_del_listener() to check ->valid = T.

I think the current logic is wrong. If s was claimed is invalid, it then points to a dead task, it is unknown when it died. As the same pid can be reused by a new process BEFORE add_del_listener(), it is unknown whether ->pid points to the actual owner task.

Rather than optimizing the wrong algorithm it's better to change the processes keeping way.

Thanks,

Signature · 2 lines
Vasiliy Kulikov
http://www.openwall.com - bringing security into open computing environments
#6 lore raw

Re: [PATCH 2/2] taskstats: add_del_listener() should ignore !valid listener's

Quoted 10 lines · Vasiliy Kulikov
Quoted 6 lines · Oleg Nesterov

When send_cpu_listeners() finds the orphaned listener it marks it as !valid and drops listeners->sem. Before it takes this sem for wrinting, s->pid can be reused and add_del_listener() can wrongly try to re-use this entry.

Change add_del_listener() to check ->valid = T.

I think the current logic is wrong.

me too ;)

still I think this patch makes sense anyway.

Quoted 2 lines

If s was claimed is invalid, it
then points to a dead task,

I forgot everything I knew about netlink. But, it seems, netlink_bind() can use nl_pid != current->pid. But even if this is not true,

Quoted 3 lines

it is unknown when it died. As the same pid
can be reused by a new process BEFORE add_del_listener(), it is unknown whether ->pid points to the actual owner task.

And? Why should we try to reuse the !valid entries? struct listener is just the "addr" we should send the message to. If someone calls add_listener() it should works regardless of dead entries with the same ->pid.

Quoted 2 lines

Rather than optimizing the wrong algorithm it's better to change the processes keeping way.

Not sure I understand... This is not optimizing, the patch tries to fix the unlikely (in fact mostly theoretical) and minor problem. Still this is the problem, no?

Thanks,

Oleg.

Reviewed-by
#7 lore raw

Re: [PATCH 2/2] taskstats: add_del_listener() should ignore !valid listener's

Quoted 8 lines · Oleg Nesterov
Quoted 3 lines

it is unknown when it died. As the same pid
can be reused by a new process BEFORE add_del_listener(), it is unknown whether ->pid points to the actual owner task.

And? Why should we try to reuse the !valid entries? struct listener is just the "addr" we should send the message to. If someone calls add_listener() it should works regardless of dead entries with the same ->pid.

True.

Quoted 5 lines
Quoted 2 lines

Rather than optimizing the wrong algorithm it's better to change the processes keeping way.

Not sure I understand... This is not optimizing, the patch tries to fix the unlikely (in fact mostly theoretical) and minor problem.

Ah, I see the problem here. Yes, then it surely makes sense.

Thanks,

Signature · 2 lines
Vasiliy Kulikov
http://www.openwall.com - bringing security into open computing environments
#8 lore raw

Re: [PATCH 2/2] taskstats: add_del_listener() should ignore !valid listener's

Quoted 7 lines · Vasiliy Kulikov
Quoted 3 lines · Oleg Nesterov

Not sure I understand... This is not optimizing, the patch tries to fix the unlikely (in fact mostly theoretical) and minor problem.

Ah, I see the problem here. Yes, then it surely makes sense.

Yes, agreed.

Just in case... please note that "s->pid can be reused" doesn't necessarily mean this pid was reused by kernel. For example, a task can close the socket without DEREGISTER, then create another one and do REGISTER which can race with send_cpu_listeners(). Afaics.

All this register/deregister logic doesn't look very right, imho.

Quoted 1 line

Thanks,

Oleg.

maintainer Acked-by on v1 2/2
#9 lore raw

Re: [PATCH 2/2] taskstats: add_del_listener() should ignore !valid listener's

In reply to Oleg Nesterov
Quoted 8 lines · Oleg Nesterov

When send_cpu_listeners() finds the orphaned listener it marks it as !valid and drops listeners->sem. Before it takes this sem for wrinting, s->pid can be reused and add_del_listener() can wrongly try to re-use this entry.

Change add_del_listener() to check ->valid = T.

BTW, some namespace changes are pending from my side. I need to get it, its on my TODO list

Balbir Singh