Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix integer overflow exploit in queue_init() leading to heap overflow. This was a complex exploit to fix because analysis of the code showed there were multiple ways this exploit could be triggered, in addition to the exploit vector documented in the CVE. Exploit: Data_buffer_size and fragment_buffer_size are added together and passed to the queue_init function defined as: struct queue *queue_init(int size) Unfortunately, data_buffer_size and fragment_buffer_size are ints, and contain values supplied by the user. The addition of these values can overflow, leading to a negative size passed to queue_init() In queue_init, space is allocated by queue->data = malloc(sizeof(void *) * (size + 1)); Given a sufficiently large negative size, (size + 1) * sizeof(void *) can overflow leading to a small positive number, leading to a small amount of buffer space being created. In queue_get, the read pointer is moved through the buffer space by queue->readp = (queue->readp + 1) % queue->size; The negative sign of size is ignored, leading to readp going beyond the allocated buffer space. Analysis of exploit: The exploit above is subtle, and centres around passing a negative number to queue_init() due to integer overflow. This exploit can be fixed by adding a check for negative size passed into queue_init(). However, further analysis of the exploit shows it can be triggered much more easily. Data_buffer_size and fragment_buffer_size added together can produce a value less than MAX_INT, which when passed into queue_init() is not negative. However, this less than MAX_INT value can overflow in the malloc calculation when multipled by sizeof(void *), leading to a small positive integer. This leads to the situation where the buffer allocated is smaller than size, and subsequent heap overflow. Checking for a negative number in queue_init() is insufficient to determine if overflow has or will occur. In fact any one time "spot checks" on size is fraught with the problem it can fail to detect previous overflow (i.e. previous overflow which has resulted in a small positive number is impossible to distinguish from a valid small positive number), and it cannot detect if overflow will occur later. Due to this the approach to the fix is to check every operation performed on data_buffer_size, fragment_buffer_size and any values derived from them. All calculations are checked for potential overflow before they are performed. This has the following advantages: 1. It allows the code to trap exactly where the overflow takes place, leading to more descriptive error messages. 2. It ensures overflow situations do not feed through to later calculations making it more difficult to determine if overflow has occurred. 3. It ensures too large values for data and fragment buffer sizes are correctly rejected even if they don't trigger the exploits. Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk>
- Loading branch information