From 59f63342b9121b9d941d3dbd09487c953a113f6e Mon Sep 17 00:00:00 2001 From: Guillem Jover Date: Sun, 20 Aug 2017 10:03:33 +0200 Subject: 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 --- debian/changelog | 2 ++ lib/dpkg/deb-version.c | 27 +++++++++++++++++++++------ lib/dpkg/t/t-deb-version.c | 22 +++++++++++++++++++++- 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 . Closes: #846405 * Always use the binary version for the .buildinfo filename in dpkg-genbuildinfo. Reported by Raphaƫl Hertzog . + * 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 #include +#include #include #include @@ -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 #include +#include +#include + #include #include @@ -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(); } -- cgit v1.2.3