Selaa lähdekoodia

libdpkg: When parsing database files only warn on bogus versions

Those versions, although bogus, were previously accepted, so to avoid
breaking systems by making dpkg refuse to parse the status and available
files, we just make it warn for now, and will make it error out on
status file parsing later on.

We keep producing errors on the rest of version parsing to avoid newly
introduced bogosity and so that packages get fixed, while not making the
system unusable anymore.

Closes: #590885, #590896, #591692, #591885
Guillem Jover 16 vuotta sitten
vanhempi
commit
357ab38575
7 muutettua tiedostoa jossa 102 lisäystä ja 26 poistoa
  1. 3 0
      debian/changelog
  2. 5 3
      lib/dpkg/dbmodify.c
  3. 4 1
      lib/dpkg/dpkg-db.h
  4. 8 19
      lib/dpkg/fields.c
  5. 4 0
      lib/dpkg/parsedump.h
  6. 76 2
      lib/dpkg/parsehelp.c
  7. 2 1
      src/update.c

+ 3 - 0
debian/changelog

@@ -4,6 +4,9 @@ dpkg (1.15.8.4) UNRELEASED; urgency=low
   * Fix use after free segfault on update-alternatives --remove-all.
     Closes: #591653, #591654
   * Always print a massage on warning when parsing control files.
+  * On database parsing only warn on bogus versions previously accepted,
+    the other instances will keep producing errors, to avoid newly
+    introduced bogosity. Closes: #590885, #590896, #591692, #591885
 
   [ Raphaël Hertzog ]
   * Fix make -C man install so that it actually finds the manual pages

+ 5 - 3
lib/dpkg/dbmodify.c

@@ -75,7 +75,8 @@ static void cleanupdates(void) {
   struct dirent **cdlist;
   int cdn, i;
 
-  parsedb(statusfile, pdb_weakclassification, NULL,NULL,NULL);
+  parsedb(statusfile, pdb_lax_parser | pdb_weakclassification,
+          NULL, NULL, NULL);
 
   *updatefnrest = '\0';
   updateslength= -1;
@@ -86,7 +87,8 @@ static void cleanupdates(void) {
     
     for (i=0; i<cdn; i++) {
       strcpy(updatefnrest, cdlist[i]->d_name);
-      parsedb(updatefnbuf, pdb_weakclassification, NULL,NULL,NULL);
+      parsedb(updatefnbuf, pdb_lax_parser | pdb_weakclassification,
+              NULL, NULL, NULL);
       if (cstatus < msdbrw_write) free(cdlist[i]);
     }
 
@@ -254,7 +256,7 @@ modstatdb_init(const char *admindir, enum modstatdb_rw readwritereq)
     cleanupdates();
     if(!(cflags & msdbrw_noavail))
     parsedb(availablefile,
-            pdb_recordavailable|pdb_rejectstatus,
+            pdb_recordavailable | pdb_rejectstatus | pdb_lax_parser,
             NULL,NULL,NULL);
   }
 

+ 4 - 1
lib/dpkg/dpkg-db.h

@@ -234,7 +234,10 @@ enum parsedbflags {
   pdb_rejectstatus      =002, /* Throw up an error if `Status' encountered             */
   pdb_weakclassification=004, /* Ignore priority/section info if we already have any   */
   pdb_ignorefiles       =010, /* Ignore files info if we already have them             */
-  pdb_ignoreolder       =020  /* Ignore packages with older versions already read      */
+  /* Ignore packages with older versions already read. */
+  pdb_ignoreolder       =020,
+  /* Perform laxer parsing, used to transition to stricter parsing. */
+  pdb_lax_parser        =040,
 };
 
 const char *illegal_packagename(const char *p, const char **ep);

+ 8 - 19
lib/dpkg/fields.c

@@ -178,12 +178,8 @@ void f_status(struct pkginfo *pigp, struct pkginfoperfile *pifp,
 void f_version(struct pkginfo *pigp, struct pkginfoperfile *pifp,
                struct parsedb_state *ps,
                const char *value, const struct fieldinfo *fip) {
-  const char *emsg;
-  
-  emsg= parseversion(&pifp->version,value);
-  if (emsg)
-    parse_error(ps, pigp,
-                _("error in Version string `%.250s': %.250s"), value, emsg);
+  parse_db_version(ps, pigp, &pifp->version, value,
+                   _("error in Version string '%.250s'"), value);
 }  
 
 void f_revision(struct pkginfo *pigp, struct pkginfoperfile *pifp,
@@ -205,19 +201,15 @@ void f_revision(struct pkginfo *pigp, struct pkginfoperfile *pifp,
 void f_configversion(struct pkginfo *pigp, struct pkginfoperfile *pifp,
                      struct parsedb_state *ps,
                      const char *value, const struct fieldinfo *fip) {
-  const char *emsg;
-  
   if (ps->flags & pdb_rejectstatus)
     parse_error(ps, pigp,
                 _("value for `config-version' field not allowed in this context"));
   if (ps->flags & pdb_recordavailable)
     return;
 
-  emsg= parseversion(&pigp->configversion,value);
-  if (emsg)
-    parse_error(ps, pigp,
-                _("error in Config-Version string `%.250s': %.250s"),
-                value, emsg);
+  parse_db_version(ps, pigp, &pigp->configversion, value,
+                   _("error in Config-Version string '%.250s'"), value);
+
 }
 
 static void conffvalue_lastword(const char *value, const char *from,
@@ -426,12 +418,9 @@ void f_dependency(struct pkginfo *pigp, struct pkginfoperfile *pifp,
         varbufreset(&version);
         varbufaddbuf(&version, versionstart, versionlength);
         varbufaddc(&version, '\0');
-        emsg = parseversion(&dop->version, version.buf);
-        if (emsg)
-          parse_error(ps, pigp,
-                      _("`%s' field, reference to `%.255s': "
-                        "error in version: %.255s"),
-                      fip->name, depname.buf, emsg);
+        parse_db_version(ps, pigp, &dop->version, version.buf,
+                         _("'%s' field, reference to '%.255s': "
+                           "error in version"), fip->name, depname.buf);
         p++; while (isspace(*p)) p++;
       } else {
         dop->verrel= dvr_none;

+ 4 - 0
lib/dpkg/parsedump.h

@@ -65,6 +65,10 @@ struct fieldinfo {
   size_t integer;
 };
 
+void parse_db_version(struct parsedb_state *ps, const struct pkginfo *pkg,
+                      struct versionrevision *version, const char *value,
+                      const char *fmt, ...) DPKG_ATTR_PRINTF(5);
+
 void parse_error(struct parsedb_state *ps, const struct pkginfo *pigp,
                  const char *fmt, ...) DPKG_ATTR_NORET DPKG_ATTR_PRINTF(3);
 void parse_warn(struct parsedb_state *ps, const struct pkginfo *pigp,

+ 76 - 2
lib/dpkg/parsehelp.c

@@ -209,7 +209,9 @@ const char *versiondescribe
   return vb->buf;
 }
 
-const char *parseversion(struct versionrevision *rversion, const char *string) {
+static const char *
+parseversion_lax(struct versionrevision *rversion, const char *string)
+{
   char *hyphen, *colon, *eepochcolon;
   const char *end, *ptr;
   unsigned long epoch;
@@ -246,7 +248,24 @@ const char *parseversion(struct versionrevision *rversion, const char *string) {
     *hyphen++ = '\0';
   rversion->revision= hyphen ? hyphen : "";
 
-  /* Check for invalid chars in version and revision. */
+  return NULL;
+}
+
+/**
+ * Check for invalid syntax in version structure.
+ *
+ * The rest of the syntax has been already checked in parseversion_lax(). So
+ * we only do the stricter checks here.
+ *
+ * @param rversion The version to verify.
+ *
+ * @return An error string, or NULL if eveyrthing was ok.
+ */
+static const char *
+version_strict_check(struct versionrevision *rversion)
+{
+  const char *ptr;
+
   /* XXX: Would be faster to use something like cisversion and cisrevision. */
   for (ptr = rversion->version; *ptr; ptr++) {
     if (!cisdigit(*ptr) && !cisalpha(*ptr) && strchr(".-+~:", *ptr) == NULL)
@@ -260,6 +279,61 @@ const char *parseversion(struct versionrevision *rversion, const char *string) {
   return NULL;
 }
 
+const char *
+parseversion(struct versionrevision *rversion, const char *string)
+{
+  const char *emsg;
+
+  emsg = parseversion_lax(rversion, string);
+  if (emsg)
+    return emsg;
+
+  return version_strict_check(rversion);
+}
+
+/**
+ * Parse a version string coming from a database file.
+ *
+ * It parses a version string, and prints a warning or an error depending
+ * on the parse options.
+ *
+ * @param ps The parsedb state.
+ * @param pkg The package being parsed.
+ * @param version The version to parse into.
+ * @param value The version string to parse from.
+ * @param fmt The error format string.
+ */
+void
+parse_db_version(struct parsedb_state *ps, const struct pkginfo *pkg,
+                 struct versionrevision *version, const char *value,
+                 const char *fmt, ...)
+{
+  const char *msg;
+  bool warn_msg = false;
+
+  msg = parseversion_lax(version, value);
+  if (msg == NULL) {
+    msg = version_strict_check(version);
+    if (ps->flags & pdb_lax_parser)
+      warn_msg = true;
+  }
+
+  if (msg) {
+    va_list args;
+    char buf[1000];
+
+    va_start(args, fmt);
+    vsnprintf(buf, sizeof(buf), fmt, args);
+
+    if (warn_msg)
+      parse_warn(ps, pkg, "%s: %.250s", buf, msg);
+    else
+      parse_error(ps, pkg, "%s: %.250s", buf, msg);
+
+    va_end(args);
+  }
+}
+
 void
 parse_must_have_field(struct parsedb_state *ps,
                       const struct pkginfo *pigp,

+ 2 - 1
src/update.c

@@ -79,7 +79,8 @@ void updateavailable(const char *const *argv) {
   varbufaddc(&vb,0);
 
   if (cipaction->arg == act_avmerge)
-    parsedb(vb.buf, pdb_recordavailable | pdb_rejectstatus, NULL, NULL, NULL);
+    parsedb(vb.buf, pdb_recordavailable | pdb_rejectstatus | pdb_lax_parser,
+            NULL, NULL, NULL);
 
   if (cipaction->arg != act_avclear)
     count += parsedb(sourcefile,