FAILED: patch "[PATCH] ipc/mqueue.c: change __do_notify() to bypass" failed to apply to 4.14-stable tree
[email protected]
maintainerThe patch below does not apply to the 4.14-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From b5f2006144c6ae941726037120fa1001ddede784 Mon Sep 17 00:00:00 2001
From: Oleg Nesterov <[email protected]>
Date: Thu, 7 May 2020 18:35:39 -0700
Subject: [PATCH] ipc/mqueue.c: change __do_notify() to bypass
check_kill_permission()
Commit cc731525f26a ("signal: Remove kernel interal si_code magic") changed the value of SI_FROMUSER(SI_MESGQ), this means that mq_notify() no longer works if the sender doesn't have rights to send a signal.
Change __do_notify() to use do_send_sig_info() instead of kill_pid_info() to avoid check_kill_permission().
This needs the additional notify.sigev_signo != 0 check, shouldn't we change do_mq_notify() to deny sigev_signo == 0 ?
Test-case:
#include <signal.h>
#include <mqueue.h>
#include <unistd.h>
#include <sys/wait.h>
#include <assert.h>
static int notified;
static void sigh(int sig)
{
notified = 1;
}
int main(void)
{
signal(SIGIO, sigh);
int fd = mq_open("/mq", O_RDWR|O_CREAT, 0666, NULL);
assert(fd >= 0);
struct sigevent se = {
.sigev_notify = SIGEV_SIGNAL,
.sigev_signo = SIGIO,
};
assert(mq_notify(fd, &se) == 0);
if (!fork()) {
assert(setuid(1) == 0);
mq_send(fd, "",1,0);
return 0;
}
wait(NULL);
mq_unlink("/mq");
assert(notified);
return 0;
}ipc/mqueue.c +26 -8
@@ -142,6 +142,7 @@ struct mqueue_inode_info {struct sigevent notify;struct pid *notify_owner; +u32 notify_self_exec_id;struct user_namespace *notify_user_ns;struct user_struct *user;/* user who created, for accounting */ struct sock *notify_sock;
@@ -773,28 +774,44 @@ static void __do_notify(struct mqueue_inode_info *info)* synchronously. */ if (info->notify_owner && info->attr.mq_curmsgs ==1 ) { -struct kernel_siginfo sig_i;switch (info->notify.sigev_notify) {case SIGEV_NONE :break ; -case SIGEV_SIGNAL : -/* sends signal */ +case SIGEV_SIGNAL : { +struct kernel_siginfo sig_i; +struct task_struct *task; + +/* do_mq_notify() accepts sigev_signo == 0, why?? */ +if (!info->notify.sigev_signo) +break ;clear_siginfo (&sig_i); sig_i.si_signo = info->notify.sigev_signo; sig_i.si_errno =0 ; sig_i.si_code =SI_MESGQ ; sig_i.si_value = info->notify.sigev_value; -/* map current pid/uid into info->owner's namespaces */ rcu_read_lock (); +/* map current pid/uid into info->owner's namespaces */ sig_i.si_pid =task_tgid_nr_ns (current,ns_of_pid (info->notify_owner)); - sig_i.si_uid =from_kuid_munged (info->notify_user_ns,current_uid ()); + sig_i.si_uid =from_kuid_munged (info->notify_user_ns, +current_uid ()); +/* +* We can't use kill_pid_info(), this signal should +* bypass check_kill_permission(). It is from kernel +* but si_fromuser() can't know this. +* We do check the self_exec_id, to avoid sending +* signals to programs that don't expect them. +*/ + task =pid_task (info->notify_owner,PIDTYPE_TGID ); +if (task && task->self_exec_id == + info->notify_self_exec_id) { +do_send_sig_info (info->notify.sigev_signo, + &sig_i, task,PIDTYPE_TGID ); + }rcu_read_unlock (); - -kill_pid_info (info->notify.sigev_signo, - &sig_i, info->notify_owner);break ; + }case SIGEV_THREAD :set_cookie (info->notify_cookie,NOTIFY_WOKENUP );netlink_sendskb (info->notify_sock, info->notify_cookie);
@@ -1383,6 +1400,7 @@ static int do_mq_notify(mqd_t mqdes, const struct sigevent *notification) info->notify.sigev_signo = notification->sigev_signo; info->notify.sigev_value = notification->sigev_value; info->notify.sigev_notify =SIGEV_SIGNAL ; + info->notify_self_exec_id = current->self_exec_id;break ; }