summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArnout Vandecappelle (Essensium/Mind)2016-02-14 19:04:09 +0100
committerDenys Vlasenko2016-02-26 14:36:24 +0100
commit5fa9fefddce56fab75b2e6c88c4516e2b21d2f5a (patch)
treefd83192c6bc05746002ae2a015d3c7fbbe51f97b
parent03718bb2743fbd772732a2c57c76c1c56fa9cd37 (diff)
downloadbusybox-5fa9fefddce56fab75b2e6c88c4516e2b21d2f5a.zip
busybox-5fa9fefddce56fab75b2e6c88c4516e2b21d2f5a.tar.gz
taskset: fix non-fancy cpuset printing on big-endian
The non-fancy version of the from_cpuset uses CPU_SETSIZE as if it represents the number of bytes in the cpuset, while it is actually the number of bits. This leads to out-of-bounds accesses on the cpu_set_t in the big-endian case. Basically all uses of CPU_SETSIZE have to be divided by 8. This is done correctly in the fancy version of from_cpuset. In addition, the big-endian case is completely wrong to begin with. All standard C libraries that I know of implement cpu_set_t as an unsigned long array, so both for big and little endian, the least significant bits are in the beginning of the array. Therefore, the approach taken for the little endian case is equally valid. We only need special handling for big endian when CPU_SETSIZE is large and we use an unsigned long long to get more bits out. Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--miscutils/taskset.c37
1 files changed, 18 insertions, 19 deletions
diff --git a/miscutils/taskset.c b/miscutils/taskset.c
index 100b1d9..fb352ab 100644
--- a/miscutils/taskset.c
+++ b/miscutils/taskset.c
@@ -75,27 +75,26 @@ static char *from_cpuset(cpu_set_t *mask)
#define TASKSET_PRINTF_MASK "%llx"
static unsigned long long from_cpuset(cpu_set_t *mask)
{
- char *p = (void*)mask;
+ BUILD_BUG_ON(CPU_SETSIZE < 8*sizeof(int));
- BUILD_BUG_ON(CPU_SETSIZE < sizeof(int));
-
- /* Take the least significant bits. Careful!
- * Consider both CPU_SETSIZE=4 and CPU_SETSIZE=1024 cases
+ /* Take the least significant bits. Assume cpu_set_t is
+ * implemented as an array of unsigned long or unsigned
+ * int.
*/
-#if BB_BIG_ENDIAN
- /* For big endian, it means LAST bits */
- if (CPU_SETSIZE < sizeof(long))
- p += CPU_SETSIZE - sizeof(int);
- else if (CPU_SETSIZE < sizeof(long long))
- p += CPU_SETSIZE - sizeof(long);
- else
- p += CPU_SETSIZE - sizeof(long long);
-#endif
- if (CPU_SETSIZE < sizeof(long))
- return *(unsigned*)p;
- if (CPU_SETSIZE < sizeof(long long))
- return *(unsigned long*)p;
- return *(unsigned long long*)p;
+ if (CPU_SETSIZE < 8*sizeof(long))
+ return *(unsigned*)mask;
+ if (CPU_SETSIZE < 8*sizeof(long long))
+ return *(unsigned long*)mask;
+# if BB_BIG_ENDIAN
+ if (sizeof(long long) > sizeof(long)) {
+ /* We can put two long in the long long, but they have to
+ * be swapped: the least significant word comes first in the
+ * array */
+ unsigned long *p = (void*)mask;
+ return p[0] + ((unsigned long long)p[1] << (8*sizeof(long)));
+ }
+# endif
+ return *(unsigned long long*)mask;
}
#endif