Ian Jackson writes ("dpkg fd and error handling bugs (with patch to fix)"):
> The attached patch fixes various error-handling bugs in dpkg. Most of
> them are minor but there is one particularly important bug which is
> the erroneous repeated closure of the fsys tarfile pipe fd, which
> in principle could cause arbitrary weirdness.
It turns out that that patch was wrong. tarfn.c didn't #include
dpkg.h and as a result you get implicit declarations of m_malloc. I
wish we could have -Werror back.
Anyway, here's the revised patch; disregard the previous one.
Ian.
diff --exclude='*~' -ru old/dpkg-1.14.5ubuntu14/debian/changelog
dpkg-1.14.5ubuntu12/debian/changelog
--- old/dpkg-1.14.5ubuntu14/debian/changelog 2007-09-18 17:21:02.000000000
+0100
+++ dpkg-1.14.5ubuntu12/debian/changelog 2007-09-21 19:14:07.000000000
+0100
@@ -1,3 +1,33 @@
+dpkg (1.14.5ubuntu16) gutsy; urgency=low
+
+ * Fix some portability problems revealed by compiler warnings:
+ - missing <dpkg.h> in tarfn.c, implicit declaration of m_malloc
+ - missing cast for %ld ohshite at info.c:98
+ - unused yyunput (missing %option nounput) in trigdeferred.l
+
+ -- Ian Jackson <[EMAIL PROTECTED]> Fri, 21 Sep 2007 19:03:36 +0100
+
+dpkg (1.14.5ubuntu15) gutsy; urgency=low
+
+ * Bugfixes to fd cleanup handling:
+ - avoid closing fsys tarfile pipe twice even in normal
+ operation - normally EBADF but might sometimes close some other
+ desired fd and cause hideous doom. (LP: #137191.)
+ - avoid duplicate attempts to [f]close in obscure error
+ situations which might conceiveably close wrong fds
+ - cast &fd to void* when passing to push_cleanup cu_closefd
+ - fix parse.c:parsedb to use ehflag_normaltidy in a sane way
+ - when passing &fd to push_cleanup cu_closefd, make fd always static
+ * Bugfix in trigger deferred file processing: reset lexer start state
+ when calling yyrestart (has no effect except after parsing/reading
+ errors in the deferred file).
+ * Fix some error handling bugs in tarfn.c:
+ - Avoid freeing uninitialised h.[Link]Name (can cause crash if .deb
+ becomes unreadable while we start up). (LP: #138887.)
+ - Use m_malloc instead of malloc (and ditch ad-hoc error handling).
+
+ -- Ian Jackson <[EMAIL PROTECTED]> Thu, 20 Sep 2007 18:12:20 +0100
+
dpkg (1.14.5ubuntu14) gutsy; urgency=low
* Change syntax of `processing:...' status fd outputs so
diff --exclude='*~' -ru old/dpkg-1.14.5ubuntu14/dpkg-deb/info.c
dpkg-1.14.5ubuntu12/dpkg-deb/info.c
--- old/dpkg-1.14.5ubuntu14/dpkg-deb/info.c 2007-07-04 15:26:33.000000000
+0100
+++ dpkg-1.14.5ubuntu12/dpkg-deb/info.c 2007-09-21 18:58:14.000000000 +0100
@@ -95,7 +95,7 @@
pathlen = strlen(directory) + strlen(component) + 2;
controlfile = (void *) realloc((void *) controlfile, pathlen);
if (!controlfile)
- ohshite(_("realloc failed (%ld bytes)"), pathlen);
+ ohshite(_("realloc failed (%lu bytes)"), (unsigned long)pathlen);
memset(controlfile, 0, sizeof(controlfile));
strcat(controlfile, directory);
diff --exclude='*~' -ru old/dpkg-1.14.5ubuntu14/lib/dpkg.h
dpkg-1.14.5ubuntu12/lib/dpkg.h
--- old/dpkg-1.14.5ubuntu14/lib/dpkg.h 2007-09-18 12:13:17.000000000 +0100
+++ dpkg-1.14.5ubuntu12/lib/dpkg.h 2007-09-20 16:52:46.000000000 +0100
@@ -236,6 +236,16 @@
void cu_closefd(int argc, void **argv);
void cu_closedir(int argc, void **argv);
+int ferror_fclose_pop_cleanup(FILE *f);
+ /* calls ferror and fclose on f, and pop_cleanup
+ * file= fopen
+ * push_cleanup(cu_closefile,~ehflag_normaltidy, 0,0, 1,(void*)file
+ * return is 0 on success or EOF if fclose or ferror failed
+ * all three calls are always made regardless, and in the right order
+ * so you can just call ohshite if this returns EOF
+ */
+
+
/*** from mlib.c ***/
void setcloexec(int fd, const char* fn);
diff --exclude='*~' -ru old/dpkg-1.14.5ubuntu14/lib/ehandle.c
dpkg-1.14.5ubuntu12/lib/ehandle.c
--- old/dpkg-1.14.5ubuntu14/lib/ehandle.c 2007-09-18 12:13:49.000000000
+0100
+++ dpkg-1.14.5ubuntu12/lib/ehandle.c 2007-09-20 17:03:31.000000000 +0100
@@ -330,6 +330,14 @@
if (f) fclose(f);
}
+int ferror_fclose_pop_cleanup(FILE *f) {
+ int r1, r2;
+ r1= ferror(f);
+ pop_cleanup(ehflag_normaltidy);
+ r2= fclose(f);
+ return r1 ? r1 : r2;
+}
+
void cu_closedir(int argc, void **argv) {
DIR *d= (DIR*)(argv[0]);
if (d) closedir(d);
diff --exclude='*~' -ru old/dpkg-1.14.5ubuntu14/lib/parse.c
dpkg-1.14.5ubuntu12/lib/parse.c
--- old/dpkg-1.14.5ubuntu14/lib/parse.c 2007-08-16 00:44:00.000000000 +0100
+++ dpkg-1.14.5ubuntu12/lib/parse.c 2007-09-20 17:19:53.000000000 +0100
@@ -87,7 +87,7 @@
* If donep is not null only one package's information is expected.
*/
- int fd;
+ static int fd;
struct pkginfo newpig, *pigp;
struct pkginfoperfile *newpifp, *pifp;
struct arbitraryfield *arp, **larpp;
@@ -109,7 +109,7 @@
fd= open(filename, O_RDONLY);
if (fd == -1) ohshite(_("failed to open package info file `%.255s' for
reading"),filename);
- push_cleanup(cu_parsedb,~0, NULL,0, 1,&fd);
+ push_cleanup(cu_parsedb,~ehflag_normaltidy, NULL,0, 1,&fd);
if (fstat(fd, &stat) == -1)
ohshite(_("can't stat package info file `%.255s'"),filename);
@@ -355,7 +355,6 @@
if (EOF_mmap(dataptr, endptr)) break;
if (c == '\n') lno++;
}
- pop_cleanup(0);
if (data != NULL) {
#ifdef HAVE_MMAP
munmap(data, stat.st_size);
@@ -364,6 +363,7 @@
#endif
}
free(value);
+ pop_cleanup(ehflag_normaltidy);
if (close(fd)) ohshite(_("failed to close after read: `%.255s'"),filename);
if (donep && !pdone) ohshit(_("no package information in
`%.255s'"),filename);
diff --exclude='*~' -ru old/dpkg-1.14.5ubuntu14/lib/tarfn.c
dpkg-1.14.5ubuntu12/lib/tarfn.c
--- old/dpkg-1.14.5ubuntu14/lib/tarfn.c 2007-07-04 15:26:32.000000000 +0100
+++ dpkg-1.14.5ubuntu12/lib/tarfn.c 2007-09-21 18:54:31.000000000 +0100
@@ -14,6 +14,7 @@
#include <grp.h>
#include <errno.h>
#include <tarfn.h>
+#include <dpkg.h>
struct TarHeader {
char Name[100];
@@ -61,7 +62,7 @@
char * str;
len = strnlen(s, size);
- str = malloc(len + 1);
+ str = m_malloc(len + 1);
memcpy(str, s, len);
str[len] = 0;
@@ -137,9 +138,11 @@
next_long_name = NULL;
next_long_link = NULL;
long_read = 0;
- symListBottom = symListPointer = symListTop = malloc(sizeof(symlinkList));
+ symListBottom = symListPointer = symListTop = m_malloc(sizeof(symlinkList));
symListTop->next = NULL;
+ h.Name = NULL;
+ h.LinkName = NULL;
h.UserData = userData;
while ( (status = functions->Read(userData, buffer, 512)) == 512 ) {
@@ -206,13 +209,7 @@
errno = 0;
break;
}
- if ((symListBottom->next = malloc(sizeof(symlinkList))) == NULL) {
- free(symListBottom->h.LinkName);
- free(symListBottom->h.Name);
- status = -1;
- errno = 0;
- break;
- }
+ symListBottom->next = m_malloc(sizeof(symlinkList));
symListBottom = symListBottom->next;
symListBottom->next = NULL;
status = 0;
@@ -233,12 +230,7 @@
if (*longp)
free(*longp);
- if (NULL == (*longp = (char *)malloc(h.Size))) {
- /* malloc failed, so bail */
- errno = 0;
- status = -1;
- break;
- }
+ *longp = (char *)m_malloc(h.Size);
bp = *longp;
// the way the GNU long{link,name} stuff works is like this:
diff --exclude='*~' -ru old/dpkg-1.14.5ubuntu14/lib/trigdeferred.l
dpkg-1.14.5ubuntu12/lib/trigdeferred.l
--- old/dpkg-1.14.5ubuntu14/lib/trigdeferred.l 2007-08-21 15:22:28.000000000
+0100
+++ dpkg-1.14.5ubuntu12/lib/trigdeferred.l 2007-09-21 18:58:31.000000000
+0100
@@ -24,6 +24,7 @@
%option prefix="trigdef_yy"
%option outfile="trigdeferred.c"
%option noyywrap
+%option nounput
%option batch
%option nodefault
%option perf-report
@@ -142,6 +143,7 @@
return 1;
trigdef_yyrestart(old_deferred);
+ BEGIN(0);
return 2;
}
diff --exclude='*~' -ru old/dpkg-1.14.5ubuntu14/lib/triglib.c
dpkg-1.14.5ubuntu12/lib/triglib.c
--- old/dpkg-1.14.5ubuntu14/lib/triglib.c 2007-08-16 16:52:53.000000000
+0100
+++ dpkg-1.14.5ubuntu12/lib/triglib.c 2007-09-20 17:38:53.000000000 +0100
@@ -296,9 +296,8 @@
}
if (signum > 0)
fprintf(nf,"%s\n",pkg->name);
- if (ferror(nf) || fclose(nf))
+ if (ferror_fclose_pop_cleanup(nf))
ohshite(_("unable to write new trigger interest file `%.250s'"),newfn.buf);
- pop_cleanup(ehflag_normaltidy);
if (rename(newfn.buf,trk_explicit_fn.buf))
ohshite(_("unable to install new trigger interest file `%.250s'"),
@@ -369,10 +368,9 @@
for (tfi=filetriggers.head; tfi; tfi=tfi->inoverall.next)
fprintf(nf, "%s %s\n", trigh.namenode_name(tfi->fnn), tfi->pkg->name);
- if (ferror(nf) || fclose(nf))
+ if (ferror_fclose_pop_cleanup(nf))
ohshite(_("unable to write new file triggers file `%.250s'"),
triggersnewfilefile);
- pop_cleanup(ehflag_normaltidy);
if (rename(triggersnewfilefile, triggersfilefile))
ohshite(_("unable to install new file triggers file as `%.250s'"),
diff --exclude='*~' -ru old/dpkg-1.14.5ubuntu14/src/archives.c
dpkg-1.14.5ubuntu12/src/archives.c
--- old/dpkg-1.14.5ubuntu14/src/archives.c 2007-09-18 12:17:35.000000000
+0100
+++ dpkg-1.14.5ubuntu12/src/archives.c 2007-09-20 16:56:46.000000000 +0100
@@ -367,11 +367,12 @@
int tarobject(struct TarInfo *ti) {
static struct varbuf conffderefn, hardlinkfn, symlinkfn;
+ static int fd;
const char *usename;
struct conffile *conff;
struct tarcontext *tc= (struct tarcontext*)ti->UserData;
- int statr, fd, i, existingdirectory, keepexisting;
+ int statr, i, existingdirectory, keepexisting;
size_t r;
struct stat stab, stabtmp;
char databuf[TARBLKSZ];
@@ -641,7 +642,7 @@
*/
fd= open(fnamenewvb.buf, (O_CREAT|O_EXCL|O_WRONLY), 0);
if (fd < 0) ohshite(_("unable to create `%.255s'"),ti->Name);
- push_cleanup(cu_closefd,ehflag_bombout, 0,0, 1,&fd);
+ push_cleanup(cu_closefd,ehflag_bombout, 0,0, 1,(void*)&fd);
debug(dbg_eachfiledetail,"tarobject NormalFile[01] open size=%lu",
(unsigned long)ti->Size);
{ char fnamebuf[256];
diff --exclude='*~' -ru old/dpkg-1.14.5ubuntu14/src/configure.c
dpkg-1.14.5ubuntu12/src/configure.c
--- old/dpkg-1.14.5ubuntu14/src/configure.c 2007-09-18 12:10:00.000000000
+0100
+++ dpkg-1.14.5ubuntu12/src/configure.c 2007-09-20 16:59:11.000000000 +0100
@@ -425,7 +425,7 @@
fd=open(fn,O_RDONLY);
if (fd>=0) {
- push_cleanup(cu_closefd,ehflag_bombout, 0,0, 1,&fd);
+ push_cleanup(cu_closefd,ehflag_bombout, 0,0, 1,(void*)&fd);
fd_md5(fd, hashbuf, -1, _("md5hash"));
pop_cleanup(ehflag_normaltidy); /* fd= open(cdr.buf) */
close(fd);
diff --exclude='*~' -ru old/dpkg-1.14.5ubuntu14/src/filesdb.c
dpkg-1.14.5ubuntu12/src/filesdb.c
--- old/dpkg-1.14.5ubuntu14/src/filesdb.c 2007-08-16 00:44:00.000000000
+0100
+++ dpkg-1.14.5ubuntu12/src/filesdb.c 2007-09-20 16:59:11.000000000 +0100
@@ -133,7 +133,7 @@
return;
}
- push_cleanup(cu_closefd,ehflag_bombout, 0,0, 1,&fd);
+ push_cleanup(cu_closefd,ehflag_bombout, 0,0, 1,(void*)&fd);
if(fstat(fd, &stat_buf))
ohshite("unable to stat files list file for package `%.250s'",pkg->name);
diff --exclude='*~' -ru old/dpkg-1.14.5ubuntu14/src/processarc.c
dpkg-1.14.5ubuntu12/src/processarc.c
--- old/dpkg-1.14.5ubuntu14/src/processarc.c 2007-09-18 11:35:37.000000000
+0100
+++ dpkg-1.14.5ubuntu12/src/processarc.c 2007-09-20 15:46:48.000000000
+0100
@@ -562,13 +562,12 @@
execlp(BACKEND, BACKEND, "--fsys-tarfile", filename, (char*)0);
ohshite(_("unable to exec dpkg-deb to get filesystem archive"));
}
- close(p1[1]);
+ close(p1[1]); p1[1]= -1;
newfileslist= 0; tc.newfilesp= &newfileslist;
push_cleanup(cu_fileslist,~0, 0, 0, 0);
tc.pkg= pkg;
tc.backendpipe= p1[0];
- push_cleanup(cu_closefd,~ehflag_bombout, 0,0, 1,&tc.backendpipe);
r= TarExtractor((void*)&tc, &tf);
if (r) {
@@ -578,8 +577,8 @@
ohshit(_("corrupted filesystem tarfile - corrupted package archive"));
}
}
- fd_null_copy(tc.backendpipe,-1,_("dpkg-deb: zap possible trailing zeros"));
- close(tc.backendpipe);
+ fd_null_copy(p1[0],-1,_("dpkg-deb: zap possible trailing zeros"));
+ close(p1[0]); p1[0]= -1;
waitsubproc(c1,BACKEND " --fsys-tarfile",PROCPIPE);
if (oldversionstatus == stat_halfinstalled || oldversionstatus ==
stat_unpacked) {