From 7f38a6c46848f23db109dda6b1aa9921e904da2a Mon Sep 17 00:00:00 2001 From: Jindrich Novy Date: Oct 21 2011 00:47:54 +0000 Subject: fix possible buffer overflows, integer overflows, update man page --- diff --git a/hardlink.1 b/hardlink.1 index 09fd7ac..0590e84 100644 --- a/hardlink.1 +++ b/hardlink.1 @@ -6,11 +6,11 @@ hardlink \- Consolidate duplicate files via hardlinks \fBhardlink\fP [\fB-c\fP] [\fB-n\fP] [\fB-v\fP] [\fB-vv\fP] [\fB-h\fP] directory1 [ directory2 ... ] .SH "DESCRIPTION" .PP -This manual page documents \fBhardlink\fP, a +This manual page documents \fBhardlink\fP, a program which consolidates duplicate files in one or more directories using hardlinks. .PP -\fBhardlink\fP traverses one +\fBhardlink\fP traverses one or more directories searching for duplicate files. When it finds duplicate files, it uses one of them as the master. It then removes all other duplicates and places a hardlink for each one pointing to the master file. @@ -34,8 +34,15 @@ Print every hardlinked file and bytes saved. Also print summary after hardlinkin Show help. .SH "AUTHOR" .PP -\fBhardlink\fP was written by Jakub Jelinek . +\fBhardlink\fP was written by Jakub Jelinek . .PP Man page written by Brian Long. .PP Man page updated by Jindrich Novy +.SH "BUGS" +.PP +\fBhardlink\fP assumes that its target directory trees do not change from under +it. If a directory tree does change, this may result in \fBhardlink\fP +accessing files and/or directories outside of the intended directory tree. +Thus, you must avoid running \fBhardlink\fP on potentially changing directory +trees, and especially on directory trees under control of another user. diff --git a/hardlink.c b/hardlink.c index 225c71f..51a71cf 100644 --- a/hardlink.c +++ b/hardlink.c @@ -1,24 +1,24 @@ /* Copyright (C) 2001 Red Hat, Inc. Written by Jakub Jelinek . - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - + /* Changes by Rémy Card to use constants and add option -n. */ -/* Changes by Jindrich Novy to add option -h, replace mmap(2) */ +/* Changes by Jindrich Novy to add option -h, replace mmap(2), fix overflows */ #define _GNU_SOURCE #include @@ -83,7 +83,7 @@ long long ndirs, nobjects, nregfiles, ncomp, nlinks, nsaved; void doexit(int i) { - if (verbose) { + if (verbose) { fprintf(stderr, "\n\n"); fprintf(stderr, "Directories %lld\n", ndirs); fprintf(stderr, "Objects %lld\n", nobjects); @@ -108,28 +108,59 @@ void usage(char *prog) unsigned int buf[NBUF]; char iobuf1[NIOBUF], iobuf2[NIOBUF]; -char nambuf1[NAMELEN], nambuf2[NAMELEN]; -void rf (char *name) +inline size_t add2(size_t a, size_t b) +{ + size_t sum = a + b; + if (sum < a) { + fprintf(stderr, "\nInteger overflow\n"); + doexit(5); + } + return sum; +} + +inline size_t add3(size_t a, size_t b, size_t c) +{ + return add2(add2(a, b), c); +} + +typedef struct { + char *buf; + size_t alloc; +} dynstr; + +void growstr(dynstr *str, size_t newlen) +{ + if (newlen < str->alloc) + return; + str->buf = realloc(str->buf, str->alloc = add2(newlen, 1)); + if (!str->buf) { + fprintf(stderr, "\nOut of memory 4\n"); + doexit(4); + } +} + +void rf (const char *name) { struct stat st, st2, st3; + const size_t namelen = strlen(name); nobjects++; if (lstat (name, &st)) return; if (S_ISDIR (st.st_mode)) { - d * dp = malloc(sizeof(d) + 1 + strlen (name)); + d * dp = malloc(add3(sizeof(d), namelen, 1)); if (!dp) { fprintf(stderr, "\nOut of memory 3\n"); doexit(3); } - strcpy (dp->name, name); + memcpy(dp->name, name, namelen + 1); dp->next = dirs; dirs = dp; } else if (S_ISREG (st.st_mode)) { int fd, i; f * fp, * fp2; h * hp; - char *n1, *n2; + const char *n1, *n2; int cksumsize = sizeof(buf); unsigned int cksum; time_t mtime = content_only ? 0 : st.st_mtime; @@ -146,8 +177,8 @@ void rf (char *name) } if (read (fd, buf, cksumsize) != cksumsize) { close(fd); - if (verbose > 1) - fprintf(stderr, "\r%*s\r", (int)strlen(name)+2, ""); + if (verbose > 1 && namelen <= NAMELEN) + fprintf(stderr, "\r%*s\r", (int)(namelen + 2), ""); return; } cksumsize = (cksumsize + sizeof(buf[0]) - 1) / sizeof(buf[0]); @@ -178,8 +209,8 @@ void rf (char *name) for (fp2 = fp; fp2 && fp2->cksum == cksum; fp2 = fp2->next) if (fp2->ino == st.st_ino && fp2->dev == st.st_dev) { close(fd); - if (verbose > 1) - fprintf(stderr, "\r%*s\r", (int)strlen(name)+2, ""); + if (verbose > 1 && namelen <= NAMELEN) + fprintf(stderr, "\r%*s\r", (int)(namelen + 2), ""); return; } for (fp2 = fp; fp2 && fp2->cksum == cksum; fp2 = fp2->next) @@ -221,36 +252,45 @@ void rf (char *name) n1 = fp2->name; n2 = name; if (!no_link) { - strcpy (stpcpy (nambuf2, n2), ".$$$___cleanit___$$$"); - if (rename (n2, nambuf2)) { - fprintf(stderr, "\nFailed to rename %s to %s\n", n2, nambuf2); + const char *suffix = ".$$$___cleanit___$$$"; + const size_t suffixlen = strlen(suffix); + size_t n2len = strlen(n2); + dynstr nam2 = {NULL, 0}; + growstr(&nam2, add2(n2len, suffixlen)); + memcpy(nam2.buf, n2, n2len); + memcpy(&nam2.buf[n2len], suffix, suffixlen + 1); + if (rename (n2, nam2.buf)) { + fprintf(stderr, "\nFailed to rename %s to %s\n", n2, nam2.buf); + free(nam2.buf); continue; } if (link (n1, n2)) { fprintf(stderr, "\nFailed to hardlink %s to %s\n", n1, n2); - if (rename (nambuf2, n2)) { - fprintf(stderr, "\nBad bad - failed to rename back %s to %s\n", nambuf2, n2); + if (rename (nam2.buf, n2)) { + fprintf(stderr, "\nBad bad - failed to rename back %s to %s\n", nam2.buf, n2); } close(fd); + free(nam2.buf); return; } - unlink (nambuf2); + unlink (nam2.buf); + free(nam2.buf); } nlinks++; if (st3.st_nlink > 1) { /* We actually did not save anything this time, since the link second argument had some other links as well. */ if (verbose > 1) - fprintf(stderr, "\r%*s\r%s %s to %s\n", (int)strlen(name)+2, "", (no_link ? "Would link" : "Linked"), n1, n2); + fprintf(stderr, "\r%*s\r%s %s to %s\n", (int)(((namelen > NAMELEN) ? 0 : namelen) + 2), "", (no_link ? "Would link" : "Linked"), n1, n2); } else { nsaved+=((st.st_size+4095)/4096)*4096; if (verbose > 1) - fprintf(stderr, "\r%*s\r%s %s to %s, %s %ld\n", (int)strlen(name)+2, "", (no_link ? "Would link" : "Linked"), n1, n2, (no_link ? "would save" : "saved"), st.st_size); + fprintf(stderr, "\r%*s\r%s %s to %s, %s %ld\n", (int)(((namelen > NAMELEN) ? 0 : namelen) + 2), "", (no_link ? "Would link" : "Linked"), n1, n2, (no_link ? "would save" : "saved"), st.st_size); } close(fd); return; } - fp2 = malloc(sizeof(f) + 1 + strlen (name)); + fp2 = malloc(add3(sizeof(f), namelen, 1)); if (!fp2) { fprintf(stderr, "\nOut of memory 2\n"); doexit(2); @@ -259,7 +299,7 @@ void rf (char *name) fp2->ino = st.st_ino; fp2->dev = st.st_dev; fp2->cksum = cksum; - strcpy(fp2->name, name); + memcpy(fp2->name, name, namelen + 1); if (fp) { fp2->next = fp->next; fp->next = fp2; @@ -267,8 +307,8 @@ void rf (char *name) fp2->next = hp->chain; hp->chain = fp2; } - if (verbose > 1) - fprintf(stderr, "\r%*s\r", (int)strlen(name)+2, ""); + if (verbose > 1 && namelen <= NAMELEN) + fprintf(stderr, "\r%*s\r", (int)(namelen + 2), ""); return; } } @@ -277,10 +317,7 @@ int main(int argc, char **argv) { int ch; int i; - char *p; - d * dp; - DIR *dh; - struct dirent *di; + dynstr nam1 = {NULL, 0}; while ((ch = getopt (argc, argv, "cnvh")) != -1) { switch (ch) { case 'n': @@ -302,13 +339,17 @@ int main(int argc, char **argv) for (i = optind; i < argc; i++) rf(argv[i]); while (dirs) { - dp = dirs; + DIR *dh; + struct dirent *di; + d * dp = dirs; + size_t nam1baselen = strlen(dp->name); dirs = dp->next; - strcpy (nambuf1, dp->name); + growstr(&nam1, add2(nam1baselen, 1)); + memcpy(nam1.buf, dp->name, nam1baselen); free (dp); - strcat (nambuf1, "/"); - p = strchr (nambuf1, 0); - dh = opendir (nambuf1); + nam1.buf[nam1baselen++] = '/'; + nam1.buf[nam1baselen] = 0; + dh = opendir (nam1.buf); if (dh == NULL) continue; ndirs++; @@ -321,14 +362,18 @@ int main(int argc, char **argv) continue; q = strrchr (di->d_name, '.'); if (q && strlen (q) == 7 && q != di->d_name) { - *p = 0; + nam1.buf[nam1baselen] = 0; if (verbose) - fprintf(stderr, "Skipping %s%s\n", nambuf1, di->d_name); + fprintf(stderr, "Skipping %s%s\n", nam1.buf, di->d_name); continue; } } - strcpy (p, di->d_name); - rf(nambuf1); + { + size_t subdirlen; + growstr(&nam1, add2(nam1baselen, subdirlen = strlen(di->d_name))); + memcpy(&nam1.buf[nam1baselen], di->d_name, add2(subdirlen, 1)); + } + rf(nam1.buf); } closedir(dh); } diff --git a/hardlink.spec b/hardlink.spec index ac8575e..d887271 100644 --- a/hardlink.spec +++ b/hardlink.spec @@ -1,7 +1,7 @@ Summary: Create a tree of hardlinks Name: hardlink Version: 1.0 -Release: 11%{?dist} +Release: 12%{?dist} Epoch: 1 Group: System Environment/Base URL: http://pkgs.fedoraproject.org/gitweb/?p=hardlink.git @@ -37,6 +37,10 @@ rm -rf $RPM_BUILD_ROOT %{_mandir}/man1/hardlink.1* %changelog +* Fri Oct 21 2011 Jindrich Novy - 1:1.0-12 +- fix possible buffer overflows, integer overflows (CVE-2011-3630 CVE-2011-3631 CVE-2011-3632) +- update man page + * Wed Mar 2 2011 Jindrich Novy - 1:1.0-11 - don't use mmap(2) to avoid failures on i386 with 1GB files and larger (#672917) - fix package URL (#676962)