aboutsummaryrefslogtreecommitdiffstats
path: root/ipc
diff options
context:
space:
mode:
authorMathias Krause <minipli@googlemail.com>2013-11-12 18:11:47 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2013-11-12 22:09:36 -0500
commit4e9b45a19241354daec281d7a785739829b52359 (patch)
treeaa06af392271e191e6d9c5e7941cd1edbd8332e9 /ipc
parent206fa940977260ede421151aae067e2509356116 (diff)
ipc, msg: fix message length check for negative values
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@linux-foundation.org: fix i386 min() warnings] Signed-off-by: Mathias Krause <minipli@googlemail.com> Cc: Pax Team <pageexec@freemail.hu> Cc: Davidlohr Bueso <davidlohr@hp.com> Cc: Brad Spengler <spender@grsecurity.net> Cc: Manfred Spraul <manfred@colorfullife.com> Cc: <stable@vger.kernel.org> [ v2.3.27+ -- yes, that old ;) ] Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'ipc')
-rw-r--r--ipc/msgutil.c20
-rw-r--r--ipc/util.h4
2 files changed, 12 insertions, 12 deletions
diff --git a/ipc/msgutil.c b/ipc/msgutil.c
index 491e71f2a1b8..7e7095974d54 100644
--- a/ipc/msgutil.c
+++ b/ipc/msgutil.c
@@ -41,15 +41,15 @@ struct msg_msgseg {
41 /* the next part of the message follows immediately */ 41 /* the next part of the message follows immediately */
42}; 42};
43 43
44#define DATALEN_MSG (int)(PAGE_SIZE-sizeof(struct msg_msg)) 44#define DATALEN_MSG ((size_t)PAGE_SIZE-sizeof(struct msg_msg))
45#define DATALEN_SEG (int)(PAGE_SIZE-sizeof(struct msg_msgseg)) 45#define DATALEN_SEG ((size_t)PAGE_SIZE-sizeof(struct msg_msgseg))
46 46
47 47
48static struct msg_msg *alloc_msg(int len) 48static struct msg_msg *alloc_msg(size_t len)
49{ 49{
50 struct msg_msg *msg; 50 struct msg_msg *msg;
51 struct msg_msgseg **pseg; 51 struct msg_msgseg **pseg;
52 int alen; 52 size_t alen;
53 53
54 alen = min(len, DATALEN_MSG); 54 alen = min(len, DATALEN_MSG);
55 msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL); 55 msg = kmalloc(sizeof(*msg) + alen, GFP_KERNEL);
@@ -80,12 +80,12 @@ out_err:
80 return NULL; 80 return NULL;
81} 81}
82 82
83struct msg_msg *load_msg(const void __user *src, int len) 83struct msg_msg *load_msg(const void __user *src, size_t len)
84{ 84{
85 struct msg_msg *msg; 85 struct msg_msg *msg;
86 struct msg_msgseg *seg; 86 struct msg_msgseg *seg;
87 int err = -EFAULT; 87 int err = -EFAULT;
88 int alen; 88 size_t alen;
89 89
90 msg = alloc_msg(len); 90 msg = alloc_msg(len);
91 if (msg == NULL) 91 if (msg == NULL)
@@ -117,8 +117,8 @@ out_err:
117struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst) 117struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
118{ 118{
119 struct msg_msgseg *dst_pseg, *src_pseg; 119 struct msg_msgseg *dst_pseg, *src_pseg;
120 int len = src->m_ts; 120 size_t len = src->m_ts;
121 int alen; 121 size_t alen;
122 122
123 BUG_ON(dst == NULL); 123 BUG_ON(dst == NULL);
124 if (src->m_ts > dst->m_ts) 124 if (src->m_ts > dst->m_ts)
@@ -147,9 +147,9 @@ struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst)
147 return ERR_PTR(-ENOSYS); 147 return ERR_PTR(-ENOSYS);
148} 148}
149#endif 149#endif
150int store_msg(void __user *dest, struct msg_msg *msg, int len) 150int store_msg(void __user *dest, struct msg_msg *msg, size_t len)
151{ 151{
152 int alen; 152 size_t alen;
153 struct msg_msgseg *seg; 153 struct msg_msgseg *seg;
154 154
155 alen = min(len, DATALEN_MSG); 155 alen = min(len, DATALEN_MSG);
diff --git a/ipc/util.h b/ipc/util.h
index f2f5036f2eed..59d78aa94987 100644
--- a/ipc/util.h
+++ b/ipc/util.h
@@ -148,9 +148,9 @@ int ipc_parse_version (int *cmd);
148#endif 148#endif
149 149
150extern void free_msg(struct msg_msg *msg); 150extern void free_msg(struct msg_msg *msg);
151extern struct msg_msg *load_msg(const void __user *src, int len); 151extern struct msg_msg *load_msg(const void __user *src, size_t len);
152extern struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst); 152extern struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst);
153extern int store_msg(void __user *dest, struct msg_msg *msg, int len); 153extern int store_msg(void __user *dest, struct msg_msg *msg, size_t len);
154 154
155extern void recompute_msgmni(struct ipc_namespace *); 155extern void recompute_msgmni(struct ipc_namespace *);
156 156