Bug 175683
Summary: | CVE-2005-3358 panic caused by bad args to set_mempolicy | ||
---|---|---|---|
Product: | Red Hat Enterprise Linux 4 | Reporter: | Doug Chapman <dchapman> |
Component: | kernel | Assignee: | Larry Woodman <lwoodman> |
Status: | CLOSED ERRATA | QA Contact: | Brian Brock <bbrock> |
Severity: | high | Docs Contact: | |
Priority: | medium | ||
Version: | 4.0 | CC: | jbaron, poelstra, security-response-team |
Target Milestone: | --- | Keywords: | Security |
Target Release: | --- | ||
Hardware: | ia64 | ||
OS: | Linux | ||
Whiteboard: | public=20051213,reported=20041213,source=redhat,impact=important | ||
Fixed In Version: | RHSA-2006-0101 | Doc Type: | Bug Fix |
Doc Text: | Story Points: | --- | |
Clone Of: | Environment: | ||
Last Closed: | 2006-01-17 08:36:53 UTC | Type: | --- |
Regression: | --- | Mount Type: | --- |
Documentation: | --- | CRM: | |
Verified Versions: | Category: | --- | |
oVirt Team: | --- | RHEL 7.3 requirements from Atomic Host: | |
Cloudforms Team: | --- | Target Upstream Version: | |
Embargoed: | |||
Bug Depends On: | |||
Bug Blocks: | 168430 |
Description
Doug Chapman
2005-12-13 23:06:26 UTC
Based on that comment this is likely to have no longer been an issue after 20050310, based on this update: http://linux.bkbits.net:8080/linux-2.6/cset@42307e9fp8ihEMrfaoPMp_agDevQNA Therefore 2.6 prior to 2.6.11 I think just changing get_nodes() so that it obeys the set_mempolicy() man page is the right thing to do here: It says all policies except MPOL_DEFAULT(3) require to specify the nodes they apply to in the nodemask parameter. This patch to get_nodes() will do just that which causes the set_mempolicy system call to return -EINVAL when the invalid parameters are passed in: ---------------------------------------------------------------- --- linux-2.6.9/mm/mempolicy.c.orig +++ linux-2.6.9/mm/mempolicy.c @@ -134,6 +134,8 @@ static int get_nodes(unsigned long *node --maxnode; bitmap_zero(nodes, MAX_NUMNODES); + if ((mode != MPOL_DEFAULT) && !nmask) + return -EINVAL; if (maxnode == 0 || !nmask) return 0; ---------------------------------------------------------------- I am still able to reproduce the panic however with different arguments passed to set_mempolicy. The resulting panic is identical to above, caused by this code in interleave_nodes() struct task_struct *me = current; nid = me->il_next; BUG_ON(nid >= MAX_NUMNODES); In all the dumps I have seen il_next is 256. This is set back in sys_set_mempolicy: if (new && new->policy == MPOL_INTERLEAVE) current->il_next = find_first_bit(new->v.nodes, MAX_NUMNODES); Perhaps, the best fix is something like: if (new && new->policy == MPOL_INTERLEAVE) { b = find_first_bit(new->v.nodes, MAX_NUMNODES); if (b >= MAX_NUMNODES) return -EINVAL; current->il_next = b; } Although I am sure there is a cleaner way. It is starting to look like this might be ia64 specific since find_first_bit is arch specific. Has anybody tried this elsewhere (note requires CONFIG_NUMA). I don't have access to any other systems to try this on. FYI, here is a reproducer for this latest panic. Different mask and a maxnode of 1. #include <asm/unistd.h> main(){ syscall(__NR_set_mempolicy,3,0xffffffffafee4357,1); write(1,10,8192); } I tried other values for maxnode (0, 2, 3 and 4) and it looks like it is just "1" that causes it to panic with this imporper mask. Note that the mask was just randomly generated by the test. There are likely other masks that hit this. I think I figured out what is going wrong here with the new patch. It _almost_ fixes the problem however there is still a way for the mask to end up being set to something like 2 (or anything with bit 0 not set). In get_nodes if we pass a bitmask with bit 0 not set and a maxnodes of 1: nlongs = BITS_TO_LONGS(maxnode); if ((maxnode % BITS_PER_LONG) == 0) endmask = ~0UL; else endmask = (1UL << (maxnode % BITS_PER_LONG)) - 1; endmask is now 1 ... if (copy_from_user(nodes, nmask, nlongs*sizeof(unsigned long))) return -EFAULT; nodes[nlongs-1] &= endmask; nodes was '0b10' and endmask was '0b01' & them together and we have nodes = 0 and the same problem as before. So, the original fix will work as long as we move it down to the bottom of the function and check "nodes" instead of "nmask". Or even more importantly I just realized that the check: if ((mode != MPOL_DEFAULT) && !nmask) return -EINVAL; Only checks if the user specified a null pointer and doesn't check if the data that nmask points to is valid. That is probably more likely, we are passing a pointer to a null mask. Doug, this patch appears to fix this problem. Did you get a chance to tsst it yet? -------------------------------------------------------------------------------- --- linux-2.6.9/mm/mempolicy.c.orig +++ linux-2.6.9/mm/mempolicy.c @@ -131,9 +131,12 @@ static int get_nodes(unsigned long *node unsigned long k; unsigned long nlongs; unsigned long endmask; + int i, bits = 0; --maxnode; bitmap_zero(nodes, MAX_NUMNODES); + if ((mode != MPOL_DEFAULT) && !nmask) + return -EINVAL; if (maxnode == 0 || !nmask) return 0; @@ -165,6 +168,13 @@ static int get_nodes(unsigned long *node if (copy_from_user(nodes, nmask, nlongs*sizeof(unsigned long))) return -EFAULT; nodes[nlongs-1] &= endmask; + /* the nmask can not be zero */ + for (i=0; i<nlongs; i++) + bits += !nodes[i]; + if (bits) + return -EINVAL; + + return mpol_check_policy(mode, nodes); } @@ -401,7 +411,7 @@ asmlinkage long sys_set_mempolicy(int mo mpol_free(current->mempolicy); current->mempolicy = new; if (new && new->policy == MPOL_INTERLEAVE) - current->il_next = find_first_bit(new->v.nodes, MAX_NUMNODES); + current->il_next = (find_first_bit(new->v.nodes, MAX_NUMNODES) & MAX_NUMNODES-1); return 0; } An advisory has been issued which should help the problem described in this bug report. This report is therefore being closed with a resolution of ERRATA. For more information on the solution and/or where to find the updated files, please follow the link below. You may reopen this bug report if the solution does not work for you. http://rhn.redhat.com/errata/RHSA-2006-0101.html |