@@ -1051,6 +1051,8 @@ struct task_struct {
/* Used by LSM modules for access restriction: */
void *security;
#endif
+#define ERROR_MSG_SIZE 256
+ char *error_msg;
/* CPU-specific state of this task: */
struct thread_struct thread;
@@ -1573,4 +1575,31 @@ extern long sched_getaffinity(pid_t pid, struct cpumask *mask);
#define TASK_SIZE_OF(tsk) TASK_SIZE
#endif
+/**
+ * errorf - Store supplementary error message
+ * @fmt: The format string
+ *
+ * Store the supplementary error message for the process if the process has
+ * enabled the facility.
+ */
+#define errorf(fmt, ...) \
+ do { \
+ if (current->error_msg) \
+ snprintf(current->error_msg, ERROR_MSG_SIZE, fmt, ## __VA_ARGS__); \
+ } while(0)
+
+/**
+ * invalf - Store supplementary invalid argument error message
+ * @fmt: The format string
+ *
+ * Store the supplementary error message for the process if the process has
+ * enabled the facility and return -EINVAL.
+ */
+#define invalf(fmt, ...) \
+ ({ \
+ errorf(fmt, ## __VA_ARGS__); \
+ -EINVAL; \
+ })
+
+
#endif
@@ -197,4 +197,10 @@ struct prctl_mm_map {
# define PR_CAP_AMBIENT_LOWER 3
# define PR_CAP_AMBIENT_CLEAR_ALL 4
+/*
+ * Control the supplementary error message gathering facility.
+ */
+#define PR_ERRMSG_ENABLE 48
+#define PR_ERRMSG_READ 49
+
#endif /* _LINUX_PRCTL_H */
@@ -932,6 +932,7 @@ void __noreturn do_exit(long code)
__this_cpu_add(dirty_throttle_leaks, tsk->nr_dirtied);
exit_rcu();
TASKS_RCU(__srcu_read_unlock(&tasks_rcu_exit_srcu, tasks_rcu_i));
+ kfree(tsk->error_msg);
do_task_dead();
}
@@ -1912,6 +1912,7 @@ static __latent_entropy struct task_struct *copy_process(
trace_task_newtask(p, clone_flags);
uprobe_copy_process(p, clone_flags);
+ p->error_msg = NULL;
return p;
@@ -2295,6 +2295,44 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
case PR_GET_FP_MODE:
error = GET_FP_MODE(me);
break;
+
+ case PR_ERRMSG_ENABLE:
+ switch (arg2) {
+ case 0:
+ if (!current->error_msg)
+ return 0;
+ kfree(current->error_msg);
+ current->error_msg = NULL;
+ return 1;
+ case 1:
+ if (current->error_msg)
+ return 1;
+ current->error_msg = kmalloc(ERROR_MSG_SIZE, GFP_KERNEL);
+ if (!current->error_msg)
+ return -ENOMEM;
+ current->error_msg[0] = 0;
+ return 0;
+ default:
+ error = -EINVAL;
+ break;
+ }
+ break;
+
+ case PR_ERRMSG_READ:
+ if (!arg2 || !arg3)
+ return -EINVAL;
+ if (!current->error_msg)
+ return -EINVAL;
+ if (!current->error_msg[0])
+ return -ENODATA;
+ error = strlen(current->error_msg);
+ if (arg3 < error)
+ error = arg3;
+ if (copy_to_user((char __user *)arg2, current->error_msg, error))
+ return -EFAULT;
+ current->error_msg[0] = 0;
+ return error;
+
default:
error = -EINVAL;
break;
Provide a way for the kernel to pass supplementary error messages to userspace. This will make it easier for userspace, particularly in containers to find out what went wrong during mounts and automounts, but is also made available to any other syscalls that want to use it. Two prctl() functions are added for this: (1) int old_setting = prctl(PR_ERRMSG_ENABLE, int setting); Enable (setting == 1) or disable (setting == 0) the facility. Disabling the facility clears the error buffer. (2) int size = prctl(PR_ERRMSG_READ, char *buffer, int buf_size); Reads the next error string into the buffer. The string is truncated if it won't fit. Strings are discarded as they're read. If there isn't a string, ENODATA is indicated. I've done it this way rather than a proc file because procfs might not be accessible. The interface inside the kernel is a pair of macros: (*) void errorf(const char *fmt, ...); (*) int invalf(const char *fmt, ...); Both of them snprintf() the string into the current process's error message buffer if the facility is enabled. The string is truncated if it exceeds the limit. invalf() returns -EINVAL whereas errof() has no return. Note that this is very crude and could be made to store multiple strings, allocate storage as required and not duplicate unformatted strings that are stored in the rodata section (like kvasprintf_const). Unfortunately, specially handling rodata strings wouldn't gain a lot as most strings are likely to be in modules, where the string's life can be terminated by rmmod. Signed-off-by: David Howells <dhowells@redhat.com> --- include/linux/sched.h | 29 +++++++++++++++++++++++++++++ include/uapi/linux/prctl.h | 6 ++++++ kernel/exit.c | 1 + kernel/fork.c | 1 + kernel/sys.c | 38 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 75 insertions(+) -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html