summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillem Jover <guillem@debian.org>2017-08-20 10:03:33 +0200
committerGuillem Jover <guillem@debian.org>2017-08-26 10:59:37 +0200
commit59f63342b9121b9d941d3dbd09487c953a113f6e (patch)
treee15c43a79d09736f0d45c0e1f51e7b68799d0fd5
parent99b3e1c276af88327b5f9423cfdf75afead28018 (diff)
downloaddpkg-59f63342b9121b9d941d3dbd09487c953a113f6e.tar.gz
libdpkg: Fix integer overflow in deb(5) version parser
The previous code was both not checking for overflows, and triggering undefined behavior as it was overflowing a signed integer. Closes: #868356
-rw-r--r--debian/changelog2
-rw-r--r--lib/dpkg/deb-version.c27
-rw-r--r--lib/dpkg/t/t-deb-version.c22
3 files changed, 44 insertions, 7 deletions
diff --git a/debian/changelog b/debian/changelog
index c78831926..9278737b7 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -9,6 +9,8 @@ dpkg (1.19.0) UNRELEASED; urgency=medium
Based on a patch by Niels Thykier <niels@thykier.net>. Closes: #846405
* Always use the binary version for the .buildinfo filename in
dpkg-genbuildinfo. Reported by Raphaƫl Hertzog <hertzog@debian.org>.
+ * Fix integer overflow in deb(5) format version parser.
+ Closes: #868356
* Perl modules:
- Switch from Dpkg::Util to List::Util, now that the module in the
new required Perl contains the needed functions.
diff --git a/lib/dpkg/deb-version.c b/lib/dpkg/deb-version.c
index ea53a592a..cee5ddd6a 100644
--- a/lib/dpkg/deb-version.c
+++ b/lib/dpkg/deb-version.c
@@ -21,6 +21,7 @@
#include <config.h>
#include <compat.h>
+#include <limits.h>
#include <string.h>
#include <stdlib.h>
@@ -46,19 +47,33 @@ const char *
deb_version_parse(struct deb_version *version, const char *str)
{
const char *str_minor, *end;
- int major = 0;
- int minor = 0;
+ unsigned int major = 0;
+ unsigned int minor = 0;
+ unsigned int divlimit = INT_MAX / 10;
+ int modlimit = INT_MAX % 10;
- for (end = str; *end && c_isdigit(*end); end++)
- major = major * 10 + *end - '0';
+ for (end = str; *end && c_isdigit(*end); end++) {
+ int ord = *end - '0';
+
+ if (major > divlimit || (major == divlimit && ord > modlimit))
+ return _("format version with too big major component");
+
+ major = major * 10 + ord;
+ }
if (end == str)
return _("format version with empty major component");
if (*end != '.')
return _("format version has no dot");
- for (end = str_minor = end + 1; *end && c_isdigit(*end); end++)
- minor = minor * 10 + *end - '0';
+ for (end = str_minor = end + 1; *end && c_isdigit(*end); end++) {
+ int ord = *end - '0';
+
+ if (minor > divlimit || (minor == divlimit && ord > modlimit))
+ return _("format version with too big minor component");
+
+ minor = minor * 10 + ord;
+ }
if (end == str_minor)
return _("format version with empty minor component");
diff --git a/lib/dpkg/t/t-deb-version.c b/lib/dpkg/t/t-deb-version.c
index 2e069073c..fef115142 100644
--- a/lib/dpkg/t/t-deb-version.c
+++ b/lib/dpkg/t/t-deb-version.c
@@ -21,6 +21,9 @@
#include <config.h>
#include <compat.h>
+#include <limits.h>
+#include <stdio.h>
+
#include <dpkg/test.h>
#include <dpkg/deb-version.h>
@@ -28,6 +31,7 @@ static void
test_deb_version_parse(void)
{
struct deb_version v;
+ char *vs;
/* Test valid versions. */
test_pass(deb_version_parse(&v, "0.0") == NULL);
@@ -59,12 +63,28 @@ test_deb_version_parse(void)
test_fail(deb_version_parse(&v, "4.4 ") == NULL);
test_fail(deb_version_parse(&v, " 5.5 ") == NULL);
+ /* Test integer limits. */
+ if (asprintf(&vs, "%d.0", INT_MAX) < 0)
+ test_bail("cannot allocate memory for asprintf()");
+ test_pass(deb_version_parse(&v, vs) == NULL);
+ free(vs);
+
+ if (asprintf(&vs, "%d.0", INT_MAX - 1) < 0)
+ test_bail("cannot allocate memory for asprintf()");
+ test_pass(deb_version_parse(&v, vs) == NULL);
+ free(vs);
+
+ if (asprintf(&vs, "%ld.0", (long int)(1L + INT_MAX)) < 0)
+ test_bail("cannot allocate memory for asprintf()");
+ test_fail(deb_version_parse(&v, vs) == NULL);
+ free(vs);
+
/* FIXME: Complete. */
}
TEST_ENTRY(test)
{
- test_plan(21);
+ test_plan(24);
test_deb_version_parse();
}