[PATCH] [RFC] Prevent livelock by forkbomb

From: Minchan Kim
Date: Mon Mar 28 2011 - 11:52:20 EST


Recently, We discussed how to prevent forkbomb.
The thing is a trade-off between cost VS effect.

Forkbomb is a _race_ case which happes by someone's mistake
so if we have to pay cost in fast path(ex, fork, exec, exit),
It's a not good.

Now, sysrq + I kills all processes. When I tested it, I still
need rebooting to work my system really well(ex, x start)
although console works. I don't know why we need such sysrq(kill
all processes and then what we can do?)

So I decide to change sysrq + I to meet our goal which prevent
forkbomb. The rationale is following as.

Forkbomb means somethings makes repeately tasks in a short time so
system don't have a free page then it become almost livelock state.
This patch uses the characteristc of forkbomb.

When you push sysrq + I, it kills recent created tasks.
(In this version, 1 minutes). Maybe all processes included
forkbomb tasks are killed. If you can't get normal state of system
after you push sysrq + I, you can try one more. It can kill futher
recent tasks(ex, 2 minutes).

You can continue to do it until your system becomes normal state.

Signed-off-by: Minchan Kim <minchan.kim@xxxxxxxxx>
---
drivers/tty/sysrq.c | 45 ++++++++++++++++++++++++++++++++++++++++++---
include/linux/sched.h | 6 ++++++
2 files changed, 48 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index 81f1395..6fb7e18 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -329,6 +329,45 @@ static void send_sig_all(int sig)
}
}

+static void send_sig_recent(int sig)
+{
+ struct task_struct *p;
+ unsigned long task_jiffies, last_jiffies = 0;
+ bool kill = false;
+
+retry:
+ for_each_process_reverse(p) {
+ if (p->mm && !is_global_init(p) && !fatal_signal_pending(p)) {
+ /* recent created task */
+ last_jiffies = timeval_to_jiffies(p->real_start_time);
+ force_sig(sig, p);
+ break;
+ }
+ }
+
+ for_each_process_reverse(p) {
+ if (p->mm && !is_global_init(p)) {
+ task_jiffies = timeval_to_jiffies(p->real_start_time);
+ /*
+ * Kill all processes which are created recenlty
+ * (ex, 1 minutes)
+ */
+ if (task_jiffies > (last_jiffies - 60 * HZ)) {
+ force_sig(sig, p);
+ kill = true;
+ }
+ else
+ break;
+ }
+ }
+
+ /*
+ * If we can't kill anything, restart with next group.
+ */
+ if (!kill)
+ goto retry;
+}
+
static void sysrq_handle_term(int key)
{
send_sig_all(SIGTERM);
@@ -374,13 +413,13 @@ static struct sysrq_key_op sysrq_thaw_op = {

static void sysrq_handle_kill(int key)
{
- send_sig_all(SIGKILL);
+ send_sig_recent(SIGKILL);
console_loglevel = 8;
}
static struct sysrq_key_op sysrq_kill_op = {
.handler = sysrq_handle_kill,
- .help_msg = "kill-all-tasks(I)",
- .action_msg = "Kill All Tasks",
+ .help_msg = "kill-recent-tasks(I)",
+ .action_msg = "Kill Recent Tasks",
.enable_mask = SYSRQ_ENABLE_SIGNAL,
};

diff --git a/include/linux/sched.h b/include/linux/sched.h
index 777d8a5..ddd0a40 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -2194,12 +2194,18 @@ static inline unsigned long wait_task_inactive(struct task_struct *p,
}
#endif

+#define prev_task(p) \
+ list_entry_rcu((p)->tasks.prev, struct task_struct, tasks)
+
#define next_task(p) \
list_entry_rcu((p)->tasks.next, struct task_struct, tasks)

#define for_each_process(p) \
for (p = &init_task ; (p = next_task(p)) != &init_task ; )

+#define for_each_process_reverse(p) \
+ for (p = &init_task ; (p = prev_task(p)) != &init_task ; )
+
extern bool current_is_single_threaded(void);

/*
--
1.7.1


>
> Thanks,
> -Kame

--
Kind regards,
Minchan Kim
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/