[3.5.y.z extended stable] Patch "ipc, msg: fix message length check for negative values" has been added to staging queue
Mathias Krause
minipli at googlemail.com
Wed Nov 20 09:17:51 UTC 2013
On 19 November 2013 14:18, Luis Henriques <luis.henriques at canonical.com> wrote:
> This is a note to let you know that I have just added a patch titled
>
> ipc, msg: fix message length check for negative values
>
> to the linux-3.5.y-queue branch of the 3.5.y.z extended stable tree
> which can be found at:
>
> http://kernel.ubuntu.com/git?p=ubuntu/linux.git;a=shortlog;h=refs/heads/linux-3.5.y-queue
>
> If you, or anyone else, feels it should not be added to this tree, please
> reply to this email.
>
> For more information about the 3.5.y.z tree, see
> https://wiki.ubuntu.com/Kernel/Dev/ExtendedStable
>
> Thanks.
> -Luis
>
> ------
>
> From dd8a5d84708799e2e7fb1fd291695764abd1fe05 Mon Sep 17 00:00:00 2001
> From: Mathias Krause <minipli at googlemail.com>
> Date: Tue, 12 Nov 2013 15:11:47 -0800
> Subject: ipc, msg: fix message length check for negative values
>
> commit 4e9b45a19241354daec281d7a785739829b52359 upstream.
>
> On 64 bit systems the test for negative message sizes is bogus as the
> size, which may be positive when evaluated as a long, will get truncated
> to an int when passed to load_msg(). So a long might very well contain a
> positive value but when truncated to an int it would become negative.
>
> That in combination with a small negative value of msg_ctlmax (which will
> be promoted to an unsigned type for the comparison against msgsz, making
> it a big positive value and therefore make it pass the check) will lead to
> two problems: 1/ The kmalloc() call in alloc_msg() will allocate a too
> small buffer as the addition of alen is effectively a subtraction. 2/ The
> copy_from_user() call in load_msg() will first overflow the buffer with
> userland data and then, when the userland access generates an access
> violation, the fixup handler copy_user_handle_tail() will try to fill the
> remainder with zeros -- roughly 4GB. That almost instantly results in a
> system crash or reset.
>
> ,-[ Reproducer (needs to be run as root) ]--
> | #include <sys/stat.h>
> | #include <sys/msg.h>
> | #include <unistd.h>
> | #include <fcntl.h>
> |
> | int main(void) {
> | long msg = 1;
> | int fd;
> |
> | fd = open("/proc/sys/kernel/msgmax", O_WRONLY);
> | write(fd, "-1", 2);
> | close(fd);
> |
> | msgsnd(0, &msg, 0xfffffff0, IPC_NOWAIT);
> |
> | return 0;
> | }
> '---
>
> Fix the issue by preventing msgsz from getting truncated by consistently
> using size_t for the message length. This way the size checks in
> do_msgsnd() could still be passed with a negative value for msg_ctlmax but
> we would fail on the buffer allocation in that case and error out.
>
> Also change the type of m_ts from int to size_t to avoid similar nastiness
> in other code paths -- it is used in similar constructs, i.e. signed vs.
> unsigned checks. It should never become negative under normal
> circumstances, though.
>
> Setting msg_ctlmax to a negative value is an odd configuration and should
> be prevented. As that might break existing userland, it will be handled
> in a separate commit so it could easily be reverted and reworked without
> reintroducing the above described bug.
>
> Hardening mechanisms for user copy operations would have catched that bug
> early -- e.g. checking slab object sizes on user copy operations as the
> usercopy feature of the PaX patch does. Or, for that matter, detect the
> long vs. int sign change due to truncation, as the size overflow plugin
> of the very same patch does.
>
> [akpm at linux-foundation.org: fix i386 min() warnings]
> Signed-off-by: Mathias Krause <minipli at googlemail.com>
> Cc: Pax Team <pageexec at freemail.hu>
> Cc: Davidlohr Bueso <davidlohr at hp.com>
> Cc: Brad Spengler <spender at grsecurity.net>
> Cc: Manfred Spraul <manfred at colorfullife.com>
> Signed-off-by: Andrew Morton <akpm at linux-foundation.org>
> Signed-off-by: Linus Torvalds <torvalds at linux-foundation.org>
> [ luis: backported to 3.5:
> - adjusted context
> - dropped changes to alloc_msg() and copy_msg() ]
The whole point of the patch is to fix a bug induced by unintended
sign extension, so dropping that hunk nullifies the fix. Fortunately,
the sign extension bug doesn't trigger on your kernel as DATALEN_MSG
isn't signed, but unsigned. You should have noticed that already as
you had to adapt that hunk as well -- see below. ;)
> Signed-off-by: Luis Henriques <luis.henriques at canonical.com>
> ---
> include/linux/msg.h | 6 +++---
> ipc/msgutil.c | 12 ++++++------
> ipc/util.h | 4 ++--
> 3 files changed, 11 insertions(+), 11 deletions(-)
>
> diff --git a/include/linux/msg.h b/include/linux/msg.h
> index 56abf155..70fc369 100644
> --- a/include/linux/msg.h
> +++ b/include/linux/msg.h
> @@ -76,9 +76,9 @@ struct msginfo {
>
> /* one msg_msg structure for each message */
> struct msg_msg {
> - struct list_head m_list;
> - long m_type;
> - int m_ts; /* message text size */
> + struct list_head m_list;
> + long m_type;
> + size_t m_ts; /* message text size */
> struct msg_msgseg* next;
> void *security;
> /* the actual message follows immediately */
> diff --git a/ipc/msgutil.c b/ipc/msgutil.c
> index 26143d3..52be05a 100644
> --- a/ipc/msgutil.c
> +++ b/ipc/msgutil.c
> @@ -39,15 +39,15 @@ struct msg_msgseg {
> /* the next part of the message follows immediately */
> };
>
> -#define DATALEN_MSG (PAGE_SIZE-sizeof(struct msg_msg))
> -#define DATALEN_SEG (PAGE_SIZE-sizeof(struct msg_msgseg))
> +#define DATALEN_MSG ((size_t)PAGE_SIZE-sizeof(struct msg_msg))
> +#define DATALEN_SEG ((size_t)PAGE_SIZE-sizeof(struct msg_msgseg))
This change is a no-op. Albeit, the former definition of DATALEN_MSG
makes the bug not to manifest itself. So unless you intend to backport
commit 3d8fa456d5 "ipc: clamp with min()" as well, just drop the
patch.
>
> -struct msg_msg *load_msg(const void __user *src, int len)
> +struct msg_msg *load_msg(const void __user *src, size_t len)
> {
> struct msg_msg *msg;
> struct msg_msgseg **pseg;
> int err;
> - int alen;
> + size_t alen;
>
> alen = len;
> if (alen > DATALEN_MSG)
> @@ -101,9 +101,9 @@ out_err:
> return ERR_PTR(err);
> }
>
> -int store_msg(void __user *dest, struct msg_msg *msg, int len)
> +int store_msg(void __user *dest, struct msg_msg *msg, size_t len)
> {
> - int alen;
> + size_t alen;
> struct msg_msgseg *seg;
>
> alen = len;
> diff --git a/ipc/util.h b/ipc/util.h
> index 6f5c20b..0bfc934 100644
> --- a/ipc/util.h
> +++ b/ipc/util.h
> @@ -138,8 +138,8 @@ int ipc_parse_version (int *cmd);
> #endif
>
> extern void free_msg(struct msg_msg *msg);
> -extern struct msg_msg *load_msg(const void __user *src, int len);
> -extern int store_msg(void __user *dest, struct msg_msg *msg, int len);
> +extern struct msg_msg *load_msg(const void __user *src, size_t len);
> +extern int store_msg(void __user *dest, struct msg_msg *msg, size_t len);
>
> extern void recompute_msgmni(struct ipc_namespace *);
>
> --
> 1.8.3.2
>
Regards,
Mathias
More information about the kernel-team
mailing list