Too-wide load generated, possibly crossing into unmapped address
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
gcc |
Unknown
|
Unknown
|
|||
gcc-5 (Ubuntu) |
Fix Released
|
Undecided
|
Unassigned | ||
gcc-6 (Ubuntu) |
Fix Released
|
Undecided
|
Unassigned |
Bug Description
The following function
void mach_parse_
{
if (ptr[0] < 0xC0U) {
*val = ptr[0] + ptr[1];
return;
}
*val = ((unsigned long int)(ptr[0]) << 24)
| ((unsigned long int)(ptr[1]) << 16)
| ((unsigned long int)(ptr[2]) << 8)
| ptr[3];
}
generates assembly (GCC 5.4 -O2 -fPIC on x86_64) that loads four bytes at ptr first, compares the first byte with 0xC0, and then processes either two, either four bytes. It seems that it's incorrect to load all four bytes before ptr[0] value has been checked. Removing -fPIC or reducing optimisation level makes the issue go away.
Here's a demo program containing this function that crashes directly if #define MMAP, and shows ASan error in stack (nothing defined) and ASan/Valgrind error in heap (#define HEAP) version:
//#define HEAP
#define MMAP
#ifdef MMAP
#include <unistd.h>
#include <sys/mman.h>
#include <stdio.h>
#elif HEAP
#include <stdlib.h>
#endif
void
mach_parse_
{
if (ptr[0] < 0xC0U) {
*val = ptr[0] + ptr[1];
return;
}
*val = ((unsigned long int)(ptr[0]) << 24)
| ((unsigned long int)(ptr[1]) << 16)
| ((unsigned long int)(ptr[2]) << 8)
| ptr[3];
}
int main(void)
{
unsigned long int val;
#ifdef MMAP
int error;
long page_size = sysconf(
unsigned char *buf = mmap(NULL, page_size * 2, PROT_READ | PROT_WRITE,
unsigned char *ptr = buf + page_size - 2;
if (buf == MAP_FAILED)
{
return 1;
}
error = mprotect(buf + page_size, page_size, PROT_NONE);
if (error != 0)
{
return 2;
}
*ptr = 0xBF;
*(ptr + 1) = 0x10;
mach_
#elif HEAP
unsigned char *buf = malloc(16384);
unsigned char *ptr = buf + 16382;
buf[16382] = 0xBF;
buf[16383] = 0x10;
#else
unsigned char buf[2];
unsigned char *ptr = buf;
buf[0] = 0xBF;
buf[1] = 0x10;
#endif
mach_
}
$ lsb_release -rd
Description: Ubuntu 16.04.1 LTS
Release: 16.04
$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-
Thread model: posix
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~
$ dpkg -l gcc-5
Desired=
| Status=
|/ Err?=(none)
||/ Name Version Architecture Description
+++-===
ii gcc-5 5.4.0-6ubuntu1~
please recheck with GCC 6 in yakkety