Pre-release snapshot of 4.3
This is intended to be a bug fix release which is expected to be released in December. There are some code infrastructure changes, which I belive to be in preparation for a later feature release (4.4) that might happen in about 6 months (which could easily change). There are lots of fixes for better handling of bad data, only two of which have been backported into Fedora's 4.2 packages.
This commit is contained in:
parent
af10d8978d
commit
81548558b9
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,3 +5,4 @@ squashfs-4.1.tar.bz2
|
||||
/squashfs-4.2-20101223.bz2
|
||||
/squashfs-4.2-20101231.bz2
|
||||
/squashfs4.2.tar.gz
|
||||
/squashfs4.3.tar.gz
|
||||
|
@ -1,261 +0,0 @@
|
||||
From: Phillip Lougher <phillip@squashfs.org.uk>
|
||||
Date: Sat, 24 Nov 2012 02:34:48 +0000 (+0000)
|
||||
Subject: unsquashfs: fix CVE-2012-4025
|
||||
X-Git-Url: http://squashfs.git.sourceforge.net/git/gitweb.cgi?p=squashfs%2Fsquashfs;a=commitdiff_plain;h=8515b3d420f502c5c0236b86e2d6d7e3b23c190e;hp=19c38fba0be1ce949ab44310d7f49887576cc123
|
||||
|
||||
unsquashfs: fix CVE-2012-4025
|
||||
|
||||
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>
|
||||
---
|
||||
|
||||
diff --git a/squashfs-tools/squashfs_fs.h b/squashfs-tools/squashfs_fs.h
|
||||
index d1dc987..58d31f4 100644
|
||||
--- squashfs-tools/squashfs_fs.h
|
||||
+++ squashfs-tools/squashfs_fs.h
|
||||
@@ -39,6 +39,7 @@
|
||||
#define SQUASHFS_FILE_LOG 17
|
||||
|
||||
#define SQUASHFS_FILE_MAX_SIZE 1048576
|
||||
+#define SQUASHFS_FILE_MAX_LOG 20
|
||||
|
||||
/* Max number of uids and gids */
|
||||
#define SQUASHFS_IDS 65536
|
||||
--- squashfs-tools/unsquashfs.c.buffer 2012-11-25 17:07:52.237809893 -0600
|
||||
+++ squashfs-tools/unsquashfs.c 2012-11-25 17:15:24.155246275 -0600
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
#include <sys/sysinfo.h>
|
||||
#include <sys/types.h>
|
||||
+#include <limits.h>
|
||||
|
||||
struct cache *fragment_cache, *data_cache;
|
||||
struct queue *to_reader, *to_deflate, *to_writer, *from_writer;
|
||||
@@ -136,6 +137,24 @@
|
||||
}
|
||||
|
||||
|
||||
+int add_overflow(int a, int b)
|
||||
+{
|
||||
+ return (INT_MAX - a) < b;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+int shift_overflow(int a, int shift)
|
||||
+{
|
||||
+ return (INT_MAX >> shift) < a;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+int multiply_overflow(int a, int multiplier)
|
||||
+{
|
||||
+ return (INT_MAX / multiplier) < a;
|
||||
+}
|
||||
+
|
||||
+
|
||||
struct queue *queue_init(int size)
|
||||
{
|
||||
struct queue *queue = malloc(sizeof(struct queue));
|
||||
@@ -143,6 +162,10 @@
|
||||
if(queue == NULL)
|
||||
EXIT_UNSQUASH("Out of memory in queue_init\n");
|
||||
|
||||
+ if(add_overflow(size, 1) ||
|
||||
+ multiply_overflow(size + 1, sizeof(void *)))
|
||||
+ EXIT_UNSQUASH("Size too large in queue_init\n");
|
||||
+
|
||||
queue->data = malloc(sizeof(void *) * (size + 1));
|
||||
if(queue->data == NULL)
|
||||
EXIT_UNSQUASH("Out of memory in queue_init\n");
|
||||
@@ -1805,7 +1828,7 @@
|
||||
{
|
||||
int i;
|
||||
sigset_t sigmask, old_mask;
|
||||
- int all_buffers_size = fragment_buffer_size + data_buffer_size;
|
||||
+ int all_buffers_size;
|
||||
|
||||
sigemptyset(&sigmask);
|
||||
sigaddset(&sigmask, SIGINT);
|
||||
@@ -1841,6 +1864,15 @@
|
||||
EXIT_UNSQUASH("Out of memory allocating thread descriptors\n");
|
||||
deflator_thread = &thread[3];
|
||||
|
||||
+ if(add_overflow(fragment_buffer_size, data_buffer_size))
|
||||
+ EXIT_UNSQUASH("Data and fragment queues combined are"
|
||||
+ " too large\n");
|
||||
+
|
||||
+ all_buffers_size = fragment_buffer_size + data_buffer_size;
|
||||
+
|
||||
+ if(add_overflow(all_buffers_size, all_buffers_size))
|
||||
+ EXIT_UNSQUASH("Data and fragment queues combined are"
|
||||
+ " too large\n");
|
||||
to_reader = queue_init(all_buffers_size);
|
||||
to_deflate = queue_init(all_buffers_size);
|
||||
to_writer = queue_init(1000);
|
||||
@@ -1940,6 +1972,31 @@
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
+int parse_number(char *arg, int *res)
|
||||
+{
|
||||
+ char *b;
|
||||
+ long number = strtol(arg, &b, 10);
|
||||
+
|
||||
+ /* check for trailing junk after number */
|
||||
+ if(*b != '\0')
|
||||
+ return 0;
|
||||
+
|
||||
+ /* check for strtol underflow or overflow in conversion */
|
||||
+ if(number == LONG_MIN || number == LONG_MAX)
|
||||
+ return 0;
|
||||
+
|
||||
+ /* reject negative numbers as invalid */
|
||||
+ if(number < 0)
|
||||
+ return 0;
|
||||
+
|
||||
+ /* check if long result will overflow signed int */
|
||||
+ if(number > INT_MAX)
|
||||
+ return 0;
|
||||
+
|
||||
+ *res = number;
|
||||
+ return 1;
|
||||
+}
|
||||
+
|
||||
|
||||
#define VERSION() \
|
||||
printf("unsquashfs version 4.2 (2011/02/28)\n");\
|
||||
@@ -2022,8 +2079,8 @@
|
||||
} else if(strcmp(argv[i], "-data-queue") == 0 ||
|
||||
strcmp(argv[i], "-da") == 0) {
|
||||
if((++i == argc) ||
|
||||
- (data_buffer_size = strtol(argv[i], &b,
|
||||
- 10), *b != '\0')) {
|
||||
+ !parse_number(argv[i],
|
||||
+ &data_buffer_size)) {
|
||||
ERROR("%s: -data-queue missing or invalid "
|
||||
"queue size\n", argv[0]);
|
||||
exit(1);
|
||||
@@ -2036,8 +2093,8 @@
|
||||
} else if(strcmp(argv[i], "-frag-queue") == 0 ||
|
||||
strcmp(argv[i], "-fr") == 0) {
|
||||
if((++i == argc) ||
|
||||
- (fragment_buffer_size = strtol(argv[i],
|
||||
- &b, 10), *b != '\0')) {
|
||||
+ !parse_number(argv[i],
|
||||
+ &fragment_buffer_size)) {
|
||||
ERROR("%s: -frag-queue missing or invalid "
|
||||
"queue size\n", argv[0]);
|
||||
exit(1);
|
||||
@@ -2161,8 +2218,41 @@
|
||||
block_size = sBlk.s.block_size;
|
||||
block_log = sBlk.s.block_log;
|
||||
|
||||
- fragment_buffer_size <<= 20 - block_log;
|
||||
- data_buffer_size <<= 20 - block_log;
|
||||
+ /*
|
||||
+ * Sanity check block size and block log.
|
||||
+ *
|
||||
+ * Check they're within correct limits
|
||||
+ */
|
||||
+ if(block_size > SQUASHFS_FILE_MAX_SIZE ||
|
||||
+ block_log > SQUASHFS_FILE_MAX_LOG)
|
||||
+ EXIT_UNSQUASH("Block size or block_log too large."
|
||||
+ " File system is corrupt.\n");
|
||||
+
|
||||
+ /*
|
||||
+ * Check block_size and block_log match
|
||||
+ */
|
||||
+ if(block_size != (1 << block_log))
|
||||
+ EXIT_UNSQUASH("Block size and block_log do not match."
|
||||
+ " File system is corrupt.\n");
|
||||
+
|
||||
+ /*
|
||||
+ * convert from queue size in Mbytes to queue size in
|
||||
+ * blocks.
|
||||
+ *
|
||||
+ * In doing so, check that the user supplied values do not
|
||||
+ * overflow a signed int
|
||||
+ */
|
||||
+ if(shift_overflow(fragment_buffer_size, 20 - block_log))
|
||||
+ EXIT_UNSQUASH("Fragment queue size is too large\n");
|
||||
+ else
|
||||
+ fragment_buffer_size <<= 20 - block_log;
|
||||
+
|
||||
+ if(shift_overflow(data_buffer_size, 20 - block_log))
|
||||
+ EXIT_UNSQUASH("Data queue size is too large\n");
|
||||
+ else
|
||||
+ data_buffer_size <<= 20 - block_log;
|
||||
+
|
||||
+
|
||||
initialise_threads(fragment_buffer_size, data_buffer_size);
|
||||
|
||||
fragment_data = malloc(block_size);
|
@ -1,77 +0,0 @@
|
||||
From: Phillip Lougher <phillip@squashfs.org.uk>
|
||||
Date: Thu, 22 Nov 2012 04:58:39 +0000 (+0000)
|
||||
Subject: unsquashfs: fix CVE-2012-4024
|
||||
X-Git-Url: http://squashfs.git.sourceforge.net/git/gitweb.cgi?p=squashfs%2Fsquashfs;a=commitdiff_plain;h=19c38fba0be1ce949ab44310d7f49887576cc123;hp=f7bbe5a202648b505879e2570672c012498f31fb
|
||||
|
||||
unsquashfs: fix CVE-2012-4024
|
||||
|
||||
Fix potential stack overflow in get_component() where an individual
|
||||
pathname component in an extract file (specified on the command line
|
||||
or in an extract file) could exceed the 1024 byte sized targname
|
||||
allocated on the stack.
|
||||
|
||||
Fix by dynamically allocating targname rather than storing it as
|
||||
a fixed size on the stack.
|
||||
|
||||
Signed-off-by: Phillip Lougher <phillip@squashfs.org.uk>
|
||||
---
|
||||
|
||||
diff --git a/squashfs-tools/unsquashfs.c b/squashfs-tools/unsquashfs.c
|
||||
index 90ed1c2..d9d1377 100644
|
||||
--- a/squashfs-tools/unsquashfs.c
|
||||
+++ b/squashfs-tools/unsquashfs.c
|
||||
@@ -1099,15 +1099,18 @@ void squashfs_closedir(struct dir *dir)
|
||||
}
|
||||
|
||||
|
||||
-char *get_component(char *target, char *targname)
|
||||
+char *get_component(char *target, char **targname)
|
||||
{
|
||||
+ char *start;
|
||||
+
|
||||
while(*target == '/')
|
||||
target ++;
|
||||
|
||||
+ start = target;
|
||||
while(*target != '/' && *target!= '\0')
|
||||
- *targname ++ = *target ++;
|
||||
+ target ++;
|
||||
|
||||
- *targname = '\0';
|
||||
+ *targname = strndup(start, target - start);
|
||||
|
||||
return target;
|
||||
}
|
||||
@@ -1133,12 +1136,12 @@ void free_path(struct pathname *paths)
|
||||
|
||||
struct pathname *add_path(struct pathname *paths, char *target, char *alltarget)
|
||||
{
|
||||
- char targname[1024];
|
||||
+ char *targname;
|
||||
int i, error;
|
||||
|
||||
TRACE("add_path: adding \"%s\" extract file\n", target);
|
||||
|
||||
- target = get_component(target, targname);
|
||||
+ target = get_component(target, &targname);
|
||||
|
||||
if(paths == NULL) {
|
||||
paths = malloc(sizeof(struct pathname));
|
||||
@@ -1162,7 +1165,7 @@ struct pathname *add_path(struct pathname *paths, char *target, char *alltarget)
|
||||
sizeof(struct path_entry));
|
||||
if(paths->name == NULL)
|
||||
EXIT_UNSQUASH("Out of memory in add_path\n");
|
||||
- paths->name[i].name = strdup(targname);
|
||||
+ paths->name[i].name = targname;
|
||||
paths->name[i].paths = NULL;
|
||||
if(use_regex) {
|
||||
paths->name[i].preg = malloc(sizeof(regex_t));
|
||||
@@ -1195,6 +1198,8 @@ struct pathname *add_path(struct pathname *paths, char *target, char *alltarget)
|
||||
/*
|
||||
* existing matching entry
|
||||
*/
|
||||
+ free(targname);
|
||||
+
|
||||
if(paths->name[i].paths == NULL) {
|
||||
/*
|
||||
* No sub-directory which means this is the leaf
|
2
sources
2
sources
@ -1 +1 @@
|
||||
1b7a781fb4cf8938842279bd3e8ee852 squashfs4.2.tar.gz
|
||||
fc16dfed64a698fd66818e26e5f7b647 squashfs4.3.tar.gz
|
||||
|
@ -1,31 +1,25 @@
|
||||
Summary: Utility for the creation of squashfs filesystems
|
||||
Name: squashfs-tools
|
||||
Version: 4.2
|
||||
Release: 5%{?dist}
|
||||
Version: 4.3
|
||||
Release: 0.1.git0be606be%{?dist}
|
||||
License: GPLv2+
|
||||
Group: System Environment/Base
|
||||
URL: http://squashfs.sourceforge.net/
|
||||
# For now I am using a prerelease version obtained by:
|
||||
# git archive --remote git://squashfs.git.sourceforge.net/gitroot/squashfs/squashfs --format=tar --prefix=squashfs4.3/ fadb345ddcf81e2b0e8cc802dbae41260be606be | gzip > squashfs4.3.tar.gz
|
||||
Source0: http://downloads.sourceforge.net/squashfs/squashfs%{version}.tar.gz
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
|
||||
BuildRequires: zlib-devel
|
||||
BuildRequires: xz-devel
|
||||
BuildRequires: lzo-devel
|
||||
BuildRequires: libattr-devel
|
||||
# Upstream commit 19c38fba0be1ce949ab44310d7f49887576cc123 (minus version
|
||||
# date change that doesn't apply cleanly)
|
||||
Patch0: path-issue.patch
|
||||
# Upstream commit 8515b3d420f502c5c0236b86e2d6d7e3b23c190e
|
||||
# Patch needed to be adjusted to fit with the 4.2 release
|
||||
Patch1: buffer-issue.patch
|
||||
|
||||
%description
|
||||
Squashfs is a highly compressed read-only filesystem for Linux. This package
|
||||
contains the utilities for manipulating squashfs filesystems.
|
||||
|
||||
%prep
|
||||
%setup -q -n squashfs4.2
|
||||
%patch0 -p1 -b .pathname
|
||||
%patch1 -p0 -b .buffer
|
||||
%setup -q -n squashfs%{version}
|
||||
|
||||
%build
|
||||
pushd squashfs-tools
|
||||
@ -47,6 +41,12 @@ rm -rf %{buildroot}
|
||||
%{_sbindir}/unsquashfs
|
||||
|
||||
%changelog
|
||||
* Sat Dec 01 2012 Bruno Wolff III <bruno@wolff.to> - 4.3-0.1.git0be606be
|
||||
- Pre-release of 4.3 to get early testing
|
||||
- This update includes a bit of internal code infrastructure changes
|
||||
- There are lots of fixes to better handle bad data
|
||||
- The final release is expected sometime in December
|
||||
|
||||
* Sun Nov 25 2012 Bruno Wolff III <bruno@wolff.to> - 4.2-5
|
||||
- Backported fix for bz 842460 (CVE-2012-4025)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user