lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:   Thu, 17 Aug 2017 22:45:45 +0300
From:   Григорий Резников 
        <grikukan@...l.ru>
To:     akpm@...ux-foundation.org, linux-kernel@...r.kernel.org
Subject: [PATCH] Adding support of RLIMIT_CPUNS

To set time limit for process now we can use RLIMIT_CPU.
However, it has precision up to one second and it can be
too big for some purposes.

This patch adds support of RLIMIT_CPUNS, which works 
almost as RLIMIT_CPU, but has nanosecond precision.

At the moment, RLIMIT_CPU and RLIMIT_CPUNS are two
independent values, because I don't see any nice way
for them to be together.

Signed-off-by: Grigory Reznikov <grikukan@...l.ru>
---
 fs/proc/base.c                      |  1 +
 include/asm-generic/resource.h      |  1 +
 include/linux/posix-timers.h        |  1 +
 include/uapi/asm-generic/resource.h |  4 +++-
 kernel/fork.c                       | 20 +++++++++++++----
 kernel/sys.c                        | 11 ++++++++-
 kernel/time/posix-cpu-timers.c      | 45 +++++++++++++++++++++++++++++++++----
 7 files changed, 73 insertions(+), 10 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index 719c2e9..1e3049e 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -567,6 +567,7 @@ static const struct limit_names lnames[RLIM_NLIMITS] = {
 	[RLIMIT_NICE] = {"Max nice priority", NULL},
 	[RLIMIT_RTPRIO] = {"Max realtime priority", NULL},
 	[RLIMIT_RTTIME] = {"Max realtime timeout", "us"},
+	[RLIMIT_CPUNS] = {"Max cpu time", "ns"},
 };
 
 /* Display limits for a process */
diff --git a/include/asm-generic/resource.h b/include/asm-generic/resource.h
index 5e752b9..ec7b0c5 100644
--- a/include/asm-generic/resource.h
+++ b/include/asm-generic/resource.h
@@ -25,6 +25,7 @@
 	[RLIMIT_NICE]		= { 0, 0 },				\
 	[RLIMIT_RTPRIO]		= { 0, 0 },				\
 	[RLIMIT_RTTIME]		= {  RLIM_INFINITY,  RLIM_INFINITY },	\
+	[RLIMIT_CPUNS]		= {  RLIM_INFINITY,  RLIM_INFINITY },	\
 }
 
 #endif
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h
index 62839fd..0e22bde 100644
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -110,6 +110,7 @@ void posix_cpu_timers_exit_group(struct task_struct *task);
 void set_process_cpu_timer(struct task_struct *task, unsigned int clock_idx,
 			   u64 *newval, u64 *oldval);
 
+void update_rlimit_cpu_ns(struct task_struct *task, unsigned long rlim_new);
 void update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new);
 
 void posixtimer_rearm(struct siginfo *info);
diff --git a/include/uapi/asm-generic/resource.h b/include/uapi/asm-generic/resource.h
index c6d10af..a86b2f4 100644
--- a/include/uapi/asm-generic/resource.h
+++ b/include/uapi/asm-generic/resource.h
@@ -45,7 +45,9 @@
 					   0-39 for nice level 19 .. -20 */
 #define RLIMIT_RTPRIO		14	/* maximum realtime priority */
 #define RLIMIT_RTTIME		15	/* timeout for RT tasks in us */
-#define RLIM_NLIMITS		16
+#define RLIMIT_CPUNS		16	/* CPU time in ns,
+					doesn't depend on RLIMIT_CPU */
+#define RLIM_NLIMITS		17
 
 /*
  * SuS says limits have to be unsigned.
diff --git a/kernel/fork.c b/kernel/fork.c
index e075b77..33f9bbf 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1348,11 +1348,23 @@ void __cleanup_sighand(struct sighand_struct *sighand)
  */
 static void posix_cpu_timers_init_group(struct signal_struct *sig)
 {
-	unsigned long cpu_limit;
-
+	unsigned long cpu_limit, cpuns_limit, total_limit;
+	/* RLIMIT_CPU timeout, RLIMIT_CPUNS timeout and time
+	 * to closest timeout
+	 */
+
 	cpu_limit = READ_ONCE(sig->rlim[RLIMIT_CPU].rlim_cur);
-	if (cpu_limit != RLIM_INFINITY) {
-		sig->cputime_expires.prof_exp = cpu_limit * NSEC_PER_SEC;
+	cpuns_limit = READ_ONCE(sig->rlim[RLIMIT_CPUNS].rlim_cur);
+
+	total_limit = RLIM_INFINITY;
+
+	if (cpu_limit != RLIM_INFINITY)
+		total_limit = cpu_limit * NSEC_PER_SEC;
+	if (cpuns_limit != RLIM_INFINITY && cpuns_limit < total_limit)
+		total_limit = cpuns_limit;
+
+	if (total_limit != RLIM_INFINITY) {
+		sig->cputime_expires.prof_exp = total_limit;
 		sig->cputimer.running = true;
 	}
 
diff --git a/kernel/sys.c b/kernel/sys.c
index 2855ee7..539b110 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1504,6 +1504,8 @@ int do_prlimit(struct task_struct *tsk, unsigned int resource,
 			 */
 			new_rlim->rlim_cur = 1;
 		}
+		if (resource == RLIMIT_CPUNS && new_rlim->rlim_cur == 0)
+			new_rlim->rlim_cur = NSEC_PER_SEC;
 	}
 	if (!retval) {
 		if (old_rlim)
@@ -1519,10 +1521,17 @@ int do_prlimit(struct task_struct *tsk, unsigned int resource,
 	 * very long-standing error, and fixing it now risks breakage of
 	 * applications, so we live with it
 	 */
-	 if (!retval && new_rlim && resource == RLIMIT_CPU &&
+	if (!retval && new_rlim && resource == RIMIT_CPU &&
 	     new_rlim->rlim_cur != RLIM_INFINITY &&
 	     IS_ENABLED(CONFIG_POSIX_TIMERS))
 		update_rlimit_cpu(tsk, new_rlim->rlim_cur);
+
+	if (!retval && new_rlim && resource == RLIMIT_CPUNS &&
+	     new_rlim->rlim_cur != RLIM_INFINITY &&
+	     IS_ENABLED(CONFIG_POSIX_TIMERS))
+		update_rlimit_cpu_ns(tsk, new_rlim->rlim_cur);
+
+
 out:
 	read_unlock(&tasklist_lock);
 	return retval;
diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c
index a3bd5db..e4830f6 100644
--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -19,20 +19,26 @@
 static void posix_cpu_timer_rearm(struct k_itimer *timer);
 
 /*
- * Called after updating RLIMIT_CPU to run cpu timer and update
+ * Called after updating RLIMIT_CPUNS to run cpu timer and update
  * tsk->signal->cputime_expires expiration cache if necessary. Needs
  * siglock protection since other code may update expiration cache as
  * well.
  */
-void update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new)
+void update_rlimit_cpu_ns(struct task_struct *task, unsigned long rlim_new)
 {
-	u64 nsecs = rlim_new * NSEC_PER_SEC;
+	u64 nsecs = rlim_new;
 
 	spin_lock_irq(&task->sighand->siglock);
 	set_process_cpu_timer(task, CPUCLOCK_PROF, &nsecs, NULL);
 	spin_unlock_irq(&task->sighand->siglock);
 }
 
+/* Same function for RLIMIT_CPU */
+void update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new)
+{
+	update_rlimit_cpu(task, rlim_new * NSEC_PER_SEC);
+}
+
 static int check_clock(const clockid_t which_clock)
 {
 	int error = 0;
@@ -938,6 +944,9 @@ static void check_process_timers(struct task_struct *tsk,
 			 SIGPROF);
 	check_cpu_itimer(tsk, &sig->it[CPUCLOCK_VIRT], &virt_expires, utime,
 			 SIGVTALRM);
+	/*
+	 * RLIMIT_CPU check
+	 */
 	soft = READ_ONCE(sig->rlim[RLIMIT_CPU].rlim_cur);
 	if (soft != RLIM_INFINITY) {
 		unsigned long psecs = div_u64(ptime, NSEC_PER_SEC);
@@ -974,7 +983,35 @@ static void check_process_timers(struct task_struct *tsk,
 		if (!prof_expires || x < prof_expires)
 			prof_expires = x;
 	}
-
+	/*
+	 * RLIMIT_CPUNS check
+	 */
+	soft = READ_ONCE(sig->rlim[RLIMIT_CPUNS].rlim_cur);
+	if (soft != RLIM_INFINITY) {
+		unsigned long hard =
+			READ_ONCE(sig->rlim[RLIMIT_CPUNS].rlim_max);
+		if (ptime >= hard) {
+			if (print_fatal_signals) {
+				pr_info("RT Watchdog Timeout (hard): %s[%d]\n",
+					tsk->comm, task_pid_nr(tsk));
+			}
+			__group_send_sig_info(SIGKILL, SEND_SIG_PRIV, tsk);
+			return;
+		}
+		if (ptime >= soft) {
+			if (print_fatal_signals) {
+				pr_info("CPU Watchdog Timeout (soft): %s[%d]\n",
+					tsk->comm, task_pid_nr(tsk));
+			}
+			__group_send_sig_info(SIGXCPU, SEND_SIG_PRIV, tsk);
+			if (soft < hard) {
+				soft += NSEC_PER_SEC;
+				sig->rlim[RLIMIT_CPUNS].rlim_cur = soft;
+			}
+		}
+		if (!prof_expires || soft < prof_expires)
+			prof_expires = soft;
+	}
 	sig->cputime_expires.prof_exp = prof_expires;
 	sig->cputime_expires.virt_exp = virt_expires;
 	sig->cputime_expires.sched_exp = sched_expires;
-- 
2.7.4

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ