Skip to contentSkip to conversation

sched/fair: WARN and refuse to set buddy when !se->on_rq

PATCH In review Daniel Axtens 4 participants 6 messages May 10, 2017 – Jun 20, 2017 Scheduler

lore
Share & tools
lore
#0 lore raw

PATCH v2 1/1 sched/fair: WARN and refuse to set buddy when !se->on_rq

If we set a next or last buddy for a se that is not on_rq, we will end up taking a NULL pointer dereference in wakeup_preempt_entity via pick_next_task_fair.

Detect when we would be about to do that, throw a warning and then refuse to actually set it.

This has been suggested at least twice[0][1]: just do it.

[0] https://marc.info/?l=linux-kernel&m=146651668921468&w=2
[1] https://lkml.org/lkml/2016/6/16/663

Cc Konstantin Khlebnikov <[email protected]>
Cc Ben Segall <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Thomas Gleixner <[email protected]>

---

I recently had to debug a problem with these (we hadn't backported Konstantin's patches in this area) and this would have saved a lot of time/pain.

v2: use SCHED_WARN_ON to restrict when the test is run. This is a
macro for WARN_ON_ONCE, which is convenient.

---
 kernel/sched/fair.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
kernel/sched/fair.c +8 -2
index d71109321841..44b94cfe02cb 100644
@@ -6168,8 +6168,11 @@ static void set_last_buddy(struct sched_entity *se)
 	if (entity_is_task(se) && unlikely(task_of(se)->policy == SCHED_IDLE))
 		return;
 
-	for_each_sched_entity(se)
+	for_each_sched_entity(se) {
+		if (SCHED_WARN_ON(!se->on_rq))
+			return;
 		cfs_rq_of(se)->last = se;
+	}
 }
 
 static void set_next_buddy(struct sched_entity *se)
@@ -6177,8 +6180,11 @@ static void set_next_buddy(struct sched_entity *se)
 	if (entity_is_task(se) && unlikely(task_of(se)->policy == SCHED_IDLE))
 		return;
 
-	for_each_sched_entity(se)
+	for_each_sched_entity(se) {
+		if (SCHED_WARN_ON(!se->on_rq))
+			return;
 		cfs_rq_of(se)->next = se;
+	}
 }
 
 static void set_skip_buddy(struct sched_entity *se)
Signature · 1 line
2.11.0
#1 lore raw

Hi Konstantin and Peter,

Just checking if this version was OK with you - I hadn't heard anything and I noticed it's not in -next so I just wanted to check to see if there were any other changes you wanted.

Regards,
Daniel

Quoted 62 lines · Daniel Axtens

If we set a next or last buddy for a se that is not on_rq, we will end up taking a NULL pointer dereference in wakeup_preempt_entity via pick_next_task_fair.

Detect when we would be about to do that, throw a warning and then refuse to actually set it.

This has been suggested at least twice[0][1]: just do it.

[0] https://marc.info/?l=linux-kernel&m=146651668921468&w=2
[1] https://lkml.org/lkml/2016/6/16/663

Cc Konstantin Khlebnikov <[email protected]>
Cc Ben Segall <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Thomas Gleixner <[email protected]>

---

I recently had to debug a problem with these (we hadn't backported Konstantin's patches in this area) and this would have saved a lot of time/pain.

v2: use SCHED_WARN_ON to restrict when the test is run. This is a
macro for WARN_ON_ONCE, which is convenient.

---
 kernel/sched/fair.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
kernel/sched/fair.c +8 -2
index d71109321841..44b94cfe02cb 100644
@@ -6168,8 +6168,11 @@ static void set_last_buddy(struct sched_entity *se)
 	if (entity_is_task(se) && unlikely(task_of(se)->policy == SCHED_IDLE))
 		return;
 
-	for_each_sched_entity(se)
+	for_each_sched_entity(se) {
+		if (SCHED_WARN_ON(!se->on_rq))
+			return;
 		cfs_rq_of(se)->last = se;
+	}
 }
 
 static void set_next_buddy(struct sched_entity *se)
@@ -6177,8 +6180,11 @@ static void set_next_buddy(struct sched_entity *se)
 	if (entity_is_task(se) && unlikely(task_of(se)->policy == SCHED_IDLE))
 		return;
 
-	for_each_sched_entity(se)
+	for_each_sched_entity(se) {
+		if (SCHED_WARN_ON(!se->on_rq))
+			return;
 		cfs_rq_of(se)->next = se;
+	}
 }
 
 static void set_skip_buddy(struct sched_entity *se)
Signature · 1 line
2.11.0
#2 lore raw
Quoted 5 lines · Daniel Axtens

Hi Konstantin and Peter,

Just checking if this version was OK with you - I hadn't heard anything and I noticed it's not in -next so I just wanted to check to see if there were any other changes you wanted.

Looks good for me. Exactly that debug helped me alot at that time.

Quoted 69 lines

Regards,
Daniel

Quoted 62 lines · Daniel Axtens

If we set a next or last buddy for a se that is not on_rq, we will end up taking a NULL pointer dereference in wakeup_preempt_entity via pick_next_task_fair.

Detect when we would be about to do that, throw a warning and then refuse to actually set it.

This has been suggested at least twice[0][1]: just do it.

[0] https://marc.info/?l=linux-kernel&m=146651668921468&w=2
[1] https://lkml.org/lkml/2016/6/16/663

Cc Konstantin Khlebnikov <[email protected]>
Cc Ben Segall <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Thomas Gleixner <[email protected]>

---

I recently had to debug a problem with these (we hadn't backported Konstantin's patches in this area) and this would have saved a lot of time/pain.

v2: use SCHED_WARN_ON to restrict when the test is run. This is a
macro for WARN_ON_ONCE, which is convenient.

---
  kernel/sched/fair.c | 10 ++++++++--
  1 file changed, 8 insertions(+), 2 deletions(-)
kernel/sched/fair.c +8 -2
index d71109321841..44b94cfe02cb 100644
@@ -6168,8 +6168,11 @@ static void set_last_buddy(struct sched_entity *se)
  	if (entity_is_task(se) && unlikely(task_of(se)->policy == SCHED_IDLE))
  		return;
 
-	for_each_sched_entity(se)
+	for_each_sched_entity(se) {
+		if (SCHED_WARN_ON(!se->on_rq))
+			return;
  		cfs_rq_of(se)->last = se;
+	}
  }
 
  static void set_next_buddy(struct sched_entity *se)
@@ -6177,8 +6180,11 @@ static void set_next_buddy(struct sched_entity *se)
  	if (entity_is_task(se) && unlikely(task_of(se)->policy == SCHED_IDLE))
  		return;
 
-	for_each_sched_entity(se)
+	for_each_sched_entity(se) {
+		if (SCHED_WARN_ON(!se->on_rq))
+			return;
  		cfs_rq_of(se)->next = se;
+	}
  }
 
  static void set_skip_buddy(struct sched_entity *se)
Signature · 1 line
2.11.0
#3 lore raw

PATCH 1/1 sched/debug: Fix SCHED_WARN_ON() to return a value on !CONFIG_SCHED_DEBUG as well

In reply to Daniel Axtens
Quoted 35 lines · Daniel Axtens

If we set a next or last buddy for a se that is not on_rq, we will end up taking a NULL pointer dereference in wakeup_preempt_entity via pick_next_task_fair.

Detect when we would be about to do that, throw a warning and then refuse to actually set it.

This has been suggested at least twice[0][1]: just do it.

[0] https://marc.info/?l=linux-kernel&m=146651668921468&w=2
[1] https://lkml.org/lkml/2016/6/16/663

Cc Konstantin Khlebnikov <[email protected]>
Cc Ben Segall <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Thomas Gleixner <[email protected]>

---

I recently had to debug a problem with these (we hadn't backported Konstantin's patches in this area) and this would have saved a lot of time/pain.

v2: use SCHED_WARN_ON to restrict when the test is run. This is a
macro for WARN_ON_ONCE, which is convenient.

---
 kernel/sched/fair.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
kernel/sched/fair.c +0 -0
index d71109321841..44b94cfe02cb 100644

kernel/sched/fair.c static void set_last_buddy(struct sched_entity *se) · line 6175

Quoted 9 lines
kernel/sched/fair.c +4 -1
@@ -6168,8 +6168,11 @@ static void set_last_buddy(struct sched_entity *se)
 	if (entity_is_task(se) && unlikely(task_of(se)->policy == SCHED_IDLE))
 		return;
 
-	for_each_sched_entity(se)
+	for_each_sched_entity(se) {
+		if (SCHED_WARN_ON(!se->on_rq))
+			return;
 		cfs_rq_of(se)->last = se;
+	}

This won't build in the !CONFIG_SCHED_DEBUG case, because of the naive definition in sched.h:

#define SCHED_WARN_ON(x) ((void)(x))

That should be changed to something like:

#define SCHED_WARN_ON(x) ((void)(x))

I've applied the fix below. (untested at the moment)

Thanks,

Ingo

========================>

Quoted 1 line

From 6d3aed3d8a0573d0a6eb1160ccd0a0713f4dbc2f Mon Sep 17 00:00:00 2001

From: Ingo Molnar <[email protected]>
Date: Tue, 20 Jun 2017 12:24:42 +0200
Subject: [PATCH] sched/debug: Fix SCHED_WARN_ON() to return a value on !CONFIG_SCHED_DEBUG as well

This definition of SCHED_WARN_ON():

#define SCHED_WARN_ON(x) ((void)(x))

is not fully compatible with the 'real' WARN_ON_ONCE() primitive, as it has no return value, so it cannot be used in conditionals.

Fix it.

Cc Daniel Axtens <[email protected]>
Cc Konstantin Khlebnikov <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Mike Galbraith <[email protected]>
Cc Thomas Gleixner <[email protected]>
Cc Linus Torvalds <[email protected]>
---
 kernel/sched/sched.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/sched/sched.h +2 -2
index f2ef759a4cb6..e0329d10bdb8 100644
@@ -39,9 +39,9 @@
 #include "cpuacct.h"
 
 #ifdef CONFIG_SCHED_DEBUG
-#define SCHED_WARN_ON(x)	WARN_ONCE(x, #x)
+# define SCHED_WARN_ON(x)	WARN_ONCE(x, #x)
 #else
-#define SCHED_WARN_ON(x)	((void)(x))
+# define SCHED_WARN_ON(x)	({ (void)(x), 0; })
 #endif
 
 struct rq;
#4 lore raw

Re: [PATCH] sched/debug: Fix SCHED_WARN_ON() to return a value on !CONFIG_SCHED_DEBUG as well

Hi Ingo,

Good catch - thanks for picking that up and taking the patch!

Regards,
Daniel

Quoted 105 lines · Daniel Axtens
Quoted 36 lines

If we set a next or last buddy for a se that is not on_rq, we will end up taking a NULL pointer dereference in wakeup_preempt_entity via pick_next_task_fair.

Detect when we would be about to do that, throw a warning and then refuse to actually set it.

This has been suggested at least twice[0][1]: just do it.

[0] https://marc.info/?l=linux-kernel&m=146651668921468&w=2
[1] https://lkml.org/lkml/2016/6/16/663

Cc Konstantin Khlebnikov <[email protected]>
Cc Ben Segall <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Thomas Gleixner <[email protected]>

---

I recently had to debug a problem with these (we hadn't backported Konstantin's patches in this area) and this would have saved a lot of time/pain.

v2: use SCHED_WARN_ON to restrict when the test is run. This is a
macro for WARN_ON_ONCE, which is convenient.

---
 kernel/sched/fair.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
kernel/sched/fair.c +0 -0
index d71109321841..44b94cfe02cb 100644

kernel/sched/fair.c static void set_last_buddy(struct sched_entity *se) · line 6175

Quoted 9 lines
kernel/sched/fair.c +4 -1
@@ -6168,8 +6168,11 @@ static void set_last_buddy(struct sched_entity *se)
 	if (entity_is_task(se) && unlikely(task_of(se)->policy == SCHED_IDLE))
 		return;
 
-	for_each_sched_entity(se)
+	for_each_sched_entity(se) {
+		if (SCHED_WARN_ON(!se->on_rq))
+			return;
 		cfs_rq_of(se)->last = se;
+	}

This won't build in the !CONFIG_SCHED_DEBUG case, because of the naive definition in sched.h:

#define SCHED_WARN_ON(x) ((void)(x))

That should be changed to something like:

#define SCHED_WARN_ON(x) ((void)(x))

I've applied the fix below. (untested at the moment)

Thanks,

Ingo

========================>
From 6d3aed3d8a0573d0a6eb1160ccd0a0713f4dbc2f Mon Sep 17 00:00:00 2001
From: Ingo Molnar <[email protected]>
Date: Tue, 20 Jun 2017 12:24:42 +0200
Subject: [PATCH] sched/debug: Fix SCHED_WARN_ON() to return a value on !CONFIG_SCHED_DEBUG as well

This definition of SCHED_WARN_ON():

#define SCHED_WARN_ON(x) ((void)(x))

is not fully compatible with the 'real' WARN_ON_ONCE() primitive, as it has no return value, so it cannot be used in conditionals.

Fix it.

Cc Daniel Axtens <[email protected]>
Cc Konstantin Khlebnikov <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Mike Galbraith <[email protected]>
Cc Thomas Gleixner <[email protected]>
Cc Linus Torvalds <[email protected]>
---
 kernel/sched/sched.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/sched/sched.h +2 -2
index f2ef759a4cb6..e0329d10bdb8 100644
@@ -39,9 +39,9 @@
 #include "cpuacct.h"
 
 #ifdef CONFIG_SCHED_DEBUG
-#define SCHED_WARN_ON(x)	WARN_ONCE(x, #x)
+# define SCHED_WARN_ON(x)	WARN_ONCE(x, #x)
 #else
-#define SCHED_WARN_ON(x)	((void)(x))
+# define SCHED_WARN_ON(x)	({ (void)(x), 0; })
 #endif
 
 struct rq;
#5 lore raw

[tip:sched/core] sched/fair: WARN() and refuse to set buddy when !se->on_rq

In reply to Daniel Axtens
sched/fair: WARN() and refuse to set buddy when !se->on_rq
Commit-ID:  c5ae366e12b2bd56fc7d7e9d484836bec9ddc110
Gitweb:     http://git.kernel.org/tip/c5ae366e12b2bd56fc7d7e9d484836bec9ddc110
Author:     Daniel Axtens <[email protected]>
AuthorDate: Thu, 11 May 2017 06:11:39 +1000
Committer:  Ingo Molnar <[email protected]>
CommitDate: Tue, 20 Jun 2017 12:26:52 +0200

sched/fair: WARN() and refuse to set buddy when !se->on_rq

If we set a next or last buddy for a se that is not on_rq, we will end up taking a NULL pointer dereference in wakeup_preempt_entity via pick_next_task_fair.

Detect when we would be about to do that, throw a warning and then refuse to actually set it.

This has been suggested at least twice:

https://marc.info/?l=linux-kernel&m=146651668921468&w=2
https://lkml.org/lkml/2016/6/16/663

I recently had to debug a problem with these (we hadn't backported Konstantin's patches in this area) and this would have saved a lot of time/pain.

Just do it.

Cc Ben Segall <[email protected]>
Cc Konstantin Khlebnikov <[email protected]>
Cc Linus Torvalds <[email protected]>
Cc Mike Galbraith <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Thomas Gleixner <[email protected]>
---
 kernel/sched/fair.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)
kernel/sched/fair.c +8 -2
index 396bca9..cb3a3da 100644
@@ -6164,8 +6164,11 @@ static void set_last_buddy(struct sched_entity *se)
 	if (entity_is_task(se) && unlikely(task_of(se)->policy == SCHED_IDLE))
 		return;
 
-	for_each_sched_entity(se)
+	for_each_sched_entity(se) {
+		if (SCHED_WARN_ON(!se->on_rq))
+			return;
 		cfs_rq_of(se)->last = se;
+	}
 }
 
 static void set_next_buddy(struct sched_entity *se)
@@ -6173,8 +6176,11 @@ static void set_next_buddy(struct sched_entity *se)
 	if (entity_is_task(se) && unlikely(task_of(se)->policy == SCHED_IDLE))
 		return;
 
-	for_each_sched_entity(se)
+	for_each_sched_entity(se) {
+		if (SCHED_WARN_ON(!se->on_rq))
+			return;
 		cfs_rq_of(se)->next = se;
+	}
 }
 
 static void set_skip_buddy(struct sched_entity *se)