Browse Source

dpkg: Allow readlink(2) returning a size smaller than stat(2)

Some bogus filesystems do not return the actual symlink size in st_size,
which contradicts POSIX. But allowing the case where the returned size
is smaller than the one used to allocate memory is harmless, although
suspect. Let it through, but still print a warning so that users can
install stuff but are reminded they need to get a fixed filesystem in
place.

This has affected at least ecryptfs in the past and now file-based
encryption support in ext4 on Android N.

Reported-by: Jay Freeman <saurik@saurik.com>
Guillem Jover 7 years ago
parent
commit
232c13c84b
3 changed files with 20 additions and 3 deletions
  1. 8 0
      debian/changelog
  2. 8 2
      src/archives.c
  3. 4 1
      src/configure.c

+ 8 - 0
debian/changelog

@@ -18,6 +18,14 @@ dpkg (1.18.16) UNRELEASED; urgency=medium
   * Use the new dpkg --validate-version command in dpkg-maintscript-helper,
     instead of abusing the --compare-versions command to perform version
     validation. Closes: #844701
+  * Allow readlink(2) to return a size smaller than stat(2) in dpkg. Some
+    bogus filesystems do not return the actual symlink size in st_size,
+    which contradicts POSIX. But allowing the case where the returned size
+    is smaller than the one used to allocate memory is harmless, although
+    suspect. Let it through, but still print a warning so that users can
+    install stuff but are reminded they need to get a fixed filesystem in
+    place. This has affected at least ecryptfs in the past and now
+    file-based encryption support in ext4 on Android N.
   * Perl modules:
     - Whitelist DPKG_GENSYMBOLS_CHECK_LEVEL, DPKG_ROOT, DPKG_ADMINDIR and
       DPKG_DATADIR environment variables in Dpkg::Build::Info.

+ 8 - 2
src/archives.c

@@ -546,9 +546,12 @@ tarobject_matches(struct tarcontext *tc,
     linksize = readlink(fn_old, linkname, stab->st_size + 1);
     if (linksize < 0)
       ohshite(_("unable to read link '%.255s'"), fn_old);
-    else if (linksize != stab->st_size)
+    else if (linksize > stab->st_size)
       ohshit(_("symbolic link '%.250s' size has changed from %jd to %zd"),
              fn_old, (intmax_t)stab->st_size, linksize);
+    else if (linksize < stab->st_size)
+      warning(_("symbolic link '%.250s' size has changed from %jd to %zd"),
+             fn_old, (intmax_t)stab->st_size, linksize);
     linkname[linksize] = '\0';
     if (strcmp(linkname, te->linkname) == 0) {
       free(linkname);
@@ -1033,9 +1036,12 @@ tarobject(void *ctx, struct tar_entry *ti)
       r = readlink(fnamevb.buf, symlinkfn.buf, symlinkfn.size);
       if (r < 0)
         ohshite(_("unable to read link '%.255s'"), ti->name);
-      else if (r != stab.st_size)
+      else if (r > stab.st_size)
         ohshit(_("symbolic link '%.250s' size has changed from %jd to %zd"),
                fnamevb.buf, (intmax_t)stab.st_size, r);
+      else if (r < stab.st_size)
+        warning(_("symbolic link '%.250s' size has changed from %jd to %zd"),
+               fnamevb.buf, (intmax_t)stab.st_size, r);
       varbuf_trunc(&symlinkfn, r);
       varbuf_end_str(&symlinkfn);
       if (symlink(symlinkfn.buf,fnametmpvb.buf))

+ 4 - 1
src/configure.c

@@ -761,7 +761,10 @@ conffderef(struct pkginfo *pkg, struct varbuf *result, const char *in)
 				warning(_("symbolic link '%.250s' size has "
 				          "changed from %jd to %zd"),
 				        result->buf, (intmax_t)stab.st_size, r);
-				return -1;
+				/* If the returned size is smaller, let's
+				 * proceed, otherwise error out. */
+				if (r > stab.st_size)
+					return -1;
 			}
 			varbuf_trunc(&target, r);
 			varbuf_end_str(&target);