Re: md5: convert from fgetln(3) to getline(3)

2018-09-05 Thread Todd C. Miller
On Thu, 23 Aug 2018 11:24:54 -0500, Scott Cheloha wrote:

> On Tue, Aug 14, 2018 at 03:11:47PM -0500, Scott Cheloha wrote:
> > This patch is ok cheloha@ and I can commit if someone else
> > is ok, too.
>
> 1 week bump.  Anyone else ok?

OK millert@

 - todd



Re: md5: convert from fgetln(3) to getline(3)

2018-09-05 Thread Lauri Tirkkonen
On Thu, Aug 23 2018 11:24:54 -0500, Scott Cheloha wrote:
> On Tue, Aug 14, 2018 at 03:11:47PM -0500, Scott Cheloha wrote:
> > This patch is ok cheloha@ and I can commit if someone else
> > is ok, too.
> 
> 1 week bump.  Anyone else ok?

no takers?

-- 
Lauri Tirkkonen | lotheac @ IRCnet



Re: md5: convert from fgetln(3) to getline(3)

2018-08-23 Thread Scott Cheloha
On Tue, Aug 14, 2018 at 03:11:47PM -0500, Scott Cheloha wrote:
> This patch is ok cheloha@ and I can commit if someone else
> is ok, too.

1 week bump.  Anyone else ok?

> On Tue, Aug 14, 2018 at 10:47:07PM +0300, Lauri Tirkkonen wrote:
> > [...]
> > 
> > diff --git a/bin/md5/md5.c b/bin/md5/md5.c
> > index 84adbcf0989..1abf28cfa16 100644
> > --- a/bin/md5/md5.c
> > +++ b/bin/md5/md5.c
> > @@ -549,11 +549,11 @@ digest_filelist(const char *file, struct 
> > hash_function *defhash, int selcount,
> > int found, base64, error, cmp, i;
> > size_t algorithm_max, algorithm_min;
> > const char *algorithm;
> > -   char *filename, *checksum, *buf, *p;
> > +   char *filename, *checksum, *line, *p, *tmpline;
> > char digest[MAX_DIGEST_LEN + 1];
> > -   char *lbuf = NULL;
> > +   ssize_t linelen;
> > FILE *listfp, *fp;
> > -   size_t len, nread;
> > +   size_t len, linesize, nread;
> > int *sel_found = NULL;
> > u_char data[32 * 1024];
> > union ANY_CTX context;
> > @@ -580,20 +580,15 @@ digest_filelist(const char *file, struct 
> > hash_function *defhash, int selcount,
> > }
> >  
> > error = found = 0;
> > -   while ((buf = fgetln(listfp, ))) {
> > +   line = NULL;
> > +   linesize = 0;
> > +   while ((linelen = getline(, , listfp)) != -1) {
> > +   tmpline = line;
> > base64 = 0;
> > -   if (buf[len - 1] == '\n')
> > -   buf[len - 1] = '\0';
> > -   else {
> > -   if ((lbuf = malloc(len + 1)) == NULL)
> > -   err(1, NULL);
> > -
> > -   (void)memcpy(lbuf, buf, len);
> > -   lbuf[len] = '\0';
> > -   buf = lbuf;
> > -   }
> > -   while (isspace((unsigned char)*buf))
> > -   buf++;
> > +   if (line[linelen - 1] == '\n')
> > +   line[linelen - 1] = '\0';
> > +   while (isspace((unsigned char)*tmpline))
> > +   tmpline++;
> >  
> > /*
> >  * Crack the line into an algorithm, filename, and checksum.
> > @@ -603,11 +598,11 @@ digest_filelist(const char *file, struct 
> > hash_function *defhash, int selcount,
> >  * Fallback on GNU form:
> >  *  CHECKSUM  FILENAME
> >  */
> > -   p = strchr(buf, ' ');
> > +   p = strchr(tmpline, ' ');
> > if (p != NULL && *(p + 1) == '(') {
> > /* BSD form */
> > *p = '\0';
> > -   algorithm = buf;
> > +   algorithm = tmpline;
> > len = strlen(algorithm);
> > if (len > algorithm_max || len < algorithm_min)
> > continue;
> > @@ -658,7 +653,7 @@ digest_filelist(const char *file, struct hash_function 
> > *defhash, int selcount,
> > if ((hf = defhash) == NULL)
> > continue;
> > algorithm = hf->name;
> > -   checksum = buf;
> > +   checksum = tmpline;
> > if ((p = strchr(checksum, ' ')) == NULL)
> > continue;
> > if (hf->style == STYLE_CKSUM) {
> > @@ -725,11 +720,15 @@ digest_filelist(const char *file, struct 
> > hash_function *defhash, int selcount,
> > error = 1;
> > }
> > }
> > +   free(line);
> > +   if (ferror(listfp)) {
> > +   warn("%s: getline", file);
> > +   error = 1;
> > +   }
> > if (listfp != stdin)
> > fclose(listfp);
> > if (!found)
> > warnx("%s: no properly formatted checksum lines found", file);
> > -   free(lbuf);
> > if (sel_found != NULL) {
> > /*
> >  * Mark found files by setting them to NULL so that we can
> > 
> > -- 
> > Lauri Tirkkonen | lotheac @ IRCnet



Re: md5: convert from fgetln(3) to getline(3)

2018-08-14 Thread Scott Cheloha
This patch is ok cheloha@ and I can commit if someone else
is ok, too.

On Tue, Aug 14, 2018 at 10:47:07PM +0300, Lauri Tirkkonen wrote:
> [...]
> 
> diff --git a/bin/md5/md5.c b/bin/md5/md5.c
> index 84adbcf0989..1abf28cfa16 100644
> --- a/bin/md5/md5.c
> +++ b/bin/md5/md5.c
> @@ -549,11 +549,11 @@ digest_filelist(const char *file, struct hash_function 
> *defhash, int selcount,
>   int found, base64, error, cmp, i;
>   size_t algorithm_max, algorithm_min;
>   const char *algorithm;
> - char *filename, *checksum, *buf, *p;
> + char *filename, *checksum, *line, *p, *tmpline;
>   char digest[MAX_DIGEST_LEN + 1];
> - char *lbuf = NULL;
> + ssize_t linelen;
>   FILE *listfp, *fp;
> - size_t len, nread;
> + size_t len, linesize, nread;
>   int *sel_found = NULL;
>   u_char data[32 * 1024];
>   union ANY_CTX context;
> @@ -580,20 +580,15 @@ digest_filelist(const char *file, struct hash_function 
> *defhash, int selcount,
>   }
>  
>   error = found = 0;
> - while ((buf = fgetln(listfp, ))) {
> + line = NULL;
> + linesize = 0;
> + while ((linelen = getline(, , listfp)) != -1) {
> + tmpline = line;
>   base64 = 0;
> - if (buf[len - 1] == '\n')
> - buf[len - 1] = '\0';
> - else {
> - if ((lbuf = malloc(len + 1)) == NULL)
> - err(1, NULL);
> -
> - (void)memcpy(lbuf, buf, len);
> - lbuf[len] = '\0';
> - buf = lbuf;
> - }
> - while (isspace((unsigned char)*buf))
> - buf++;
> + if (line[linelen - 1] == '\n')
> + line[linelen - 1] = '\0';
> + while (isspace((unsigned char)*tmpline))
> + tmpline++;
>  
>   /*
>* Crack the line into an algorithm, filename, and checksum.
> @@ -603,11 +598,11 @@ digest_filelist(const char *file, struct hash_function 
> *defhash, int selcount,
>* Fallback on GNU form:
>*  CHECKSUM  FILENAME
>*/
> - p = strchr(buf, ' ');
> + p = strchr(tmpline, ' ');
>   if (p != NULL && *(p + 1) == '(') {
>   /* BSD form */
>   *p = '\0';
> - algorithm = buf;
> + algorithm = tmpline;
>   len = strlen(algorithm);
>   if (len > algorithm_max || len < algorithm_min)
>   continue;
> @@ -658,7 +653,7 @@ digest_filelist(const char *file, struct hash_function 
> *defhash, int selcount,
>   if ((hf = defhash) == NULL)
>   continue;
>   algorithm = hf->name;
> - checksum = buf;
> + checksum = tmpline;
>   if ((p = strchr(checksum, ' ')) == NULL)
>   continue;
>   if (hf->style == STYLE_CKSUM) {
> @@ -725,11 +720,15 @@ digest_filelist(const char *file, struct hash_function 
> *defhash, int selcount,
>   error = 1;
>   }
>   }
> + free(line);
> + if (ferror(listfp)) {
> + warn("%s: getline", file);
> + error = 1;
> + }
>   if (listfp != stdin)
>   fclose(listfp);
>   if (!found)
>   warnx("%s: no properly formatted checksum lines found", file);
> - free(lbuf);
>   if (sel_found != NULL) {
>   /*
>* Mark found files by setting them to NULL so that we can
> 
> -- 
> Lauri Tirkkonen | lotheac @ IRCnet



Re: md5: convert from fgetln(3) to getline(3)

2018-08-14 Thread Lauri Tirkkonen
On Tue, Aug 14 2018 14:29:30 -0500, Scott Cheloha wrote:
> On Tue, Aug 14, 2018 at 08:55:14PM +0300, Lauri Tirkkonen wrote:
> > Hi,
> > 
> > On Tue, Aug 14 2018 12:08:54 -0500, Scott Cheloha wrote:
> > > > +   len = (size_t)linelen;
> > > > +   if (line[len - 1] == '\n')
> > > > +   line[len - 1] = '\0';
> > > 
> > > You can just use linelen directly and skip the assignment.
> > 
> > I did it this way because this bit later
> > 
> > if (base64)
> > cmp = strncmp(checksum, digest, len);
> > 
> > could have been reached without assigning len (if the "could be GNU form"
> > branch is taken). It looks like checksum and digest will always be null
> > temrinated, but 'len' may be reduced to something less than linelen
> > (eg. in the case that the line starts with whitespace). Maybe there's
> > some reason to not use strcmp here?
> 
> base64 is only set in the BSD form case, and in that case len is set to
> the length of checksum before the comparison.

Thanks, I neglected to check when base64 is set. Your logic makes sense
in that case, so I applied your suggestion below.

> > But shouldn't we check *before* calling free to ensure errno is not
> > clobbered? (although, getline.3's example code certainly does err(1,
> > "getline"); after free()...)
> 
> free(3) doesn't set errno.  At least, not portably.  I definitely wouldn't
> worry about the possibility of there being a malloc implementation that
> clobbers errno.
> 
> The ordering (check or free first) doesn't matter in this case because you
> aren't returning from the function early if you have an error on listfp,
> but I think the intent of the example in the manpage is to prevent leaks
> in such cases, e.g.:
> 
>   if (ferror(listfp)) {
>   warn("getline");
>   return 1;
>   }
>   free(line); /* leak */
> 
> I would just stick to the manpage example in this case and keep the free(3)
> as close as possible to the allocation (getline).

Thanks for the explanation. Maybe I'm too used to not trusting the
platform to do things that make sense (in the past I remember thinking
that even a pathological ferror could modify errno; its manpage says it
won't though so I could trust that).

> > +   while ((linelen = getline(, , listfp)) != -1) {
> > +   char *tmpline = line;
> 
> You're already modifying the stack variable declarations up above, so
> declare tmpline alongside the other char pointers.

fixed.

> > +   if (ferror(listfp))
> > +   warn("%s: getline", file);
> > +   free(line);
> 
> Set error = 1 in the ferror case, too.

fixed.

diff --git a/bin/md5/md5.c b/bin/md5/md5.c
index 84adbcf0989..1abf28cfa16 100644
--- a/bin/md5/md5.c
+++ b/bin/md5/md5.c
@@ -549,11 +549,11 @@ digest_filelist(const char *file, struct hash_function 
*defhash, int selcount,
int found, base64, error, cmp, i;
size_t algorithm_max, algorithm_min;
const char *algorithm;
-   char *filename, *checksum, *buf, *p;
+   char *filename, *checksum, *line, *p, *tmpline;
char digest[MAX_DIGEST_LEN + 1];
-   char *lbuf = NULL;
+   ssize_t linelen;
FILE *listfp, *fp;
-   size_t len, nread;
+   size_t len, linesize, nread;
int *sel_found = NULL;
u_char data[32 * 1024];
union ANY_CTX context;
@@ -580,20 +580,15 @@ digest_filelist(const char *file, struct hash_function 
*defhash, int selcount,
}
 
error = found = 0;
-   while ((buf = fgetln(listfp, ))) {
+   line = NULL;
+   linesize = 0;
+   while ((linelen = getline(, , listfp)) != -1) {
+   tmpline = line;
base64 = 0;
-   if (buf[len - 1] == '\n')
-   buf[len - 1] = '\0';
-   else {
-   if ((lbuf = malloc(len + 1)) == NULL)
-   err(1, NULL);
-
-   (void)memcpy(lbuf, buf, len);
-   lbuf[len] = '\0';
-   buf = lbuf;
-   }
-   while (isspace((unsigned char)*buf))
-   buf++;
+   if (line[linelen - 1] == '\n')
+   line[linelen - 1] = '\0';
+   while (isspace((unsigned char)*tmpline))
+   tmpline++;
 
/*
 * Crack the line into an algorithm, filename, and checksum.
@@ -603,11 +598,11 @@ digest_filelist(const char *file, struct hash_function 
*defhash, int selcount,
 * Fallback on GNU form:
 *  CHECKSUM  FILENAME
 */
-   p = strchr(buf, ' ');
+   p = strchr(tmpline, ' ');
if (p != NULL && *(p + 1) == '(') {
/* BSD form */
*p = '\0';
-   algorithm = buf;
+   algorithm = tmpline;
len = strlen(algorithm);
  

Re: md5: convert from fgetln(3) to getline(3)

2018-08-14 Thread Scott Cheloha
On Tue, Aug 14, 2018 at 08:55:14PM +0300, Lauri Tirkkonen wrote:
> Hi,
> 
> On Tue, Aug 14 2018 12:08:54 -0500, Scott Cheloha wrote:
> > > + len = (size_t)linelen;
> > > + if (line[len - 1] == '\n')
> > > + line[len - 1] = '\0';
> > 
> > You can just use linelen directly and skip the assignment.
> 
> I did it this way because this bit later
> 
>   if (base64)
>   cmp = strncmp(checksum, digest, len);
> 
> could have been reached without assigning len (if the "could be GNU form"
> branch is taken). It looks like checksum and digest will always be null
> temrinated, but 'len' may be reduced to something less than linelen
> (eg. in the case that the line starts with whitespace). Maybe there's
> some reason to not use strcmp here?

base64 is only set in the BSD form case, and in that case len is set to
the length of checksum before the comparison.

Both linelen and len are inaccurate for the purposes of strcmp(3) and friends
after you've stubbed out the newline, anyway, so definitely use linelen directly
when you stub out the newline and skip assigning to len.

As for strcmp(3), I'm unsure.  b64_ntop() seems to NUL-terminate the result and
we're looking at the full checksum string, so a switch to strcmp(3) might make
sense.  If it does make sense it should happen in a separate diff, though.

> 
> > > + while (isspace((unsigned char)*line))
> > > + line++;
> > 
> > Incrementing the returned pointer works for fgetln(3) because it
> > keeps track of its own buffer internally.  But if you try the same
> > trick with getline(3) it may try to realloc your invalid pointer.
> 
> Ah, of course. Stupid mistake. Fixed below.

No worries, I hadn't seen it before either.  That lengthy
explanation was partly so I was sure I understood it myself.

> > > @@ -725,11 +720,11 @@ digest_filelist(const char *file, struct 
> > > hash_function *defhash, int selcount,
> > >   error = 1;
> > >   }
> > >   }
> > > + free(line);
> > 
> > getline(3) can fail, too, so you need to check listfp for errors after
> > freeing line.
> 
> So can fgetln(3), but fair enough.

Yeah it's missing, so we kill two birds with one stone by converting
the code :)

> But shouldn't we check *before* calling free to ensure errno is not
> clobbered? (although, getline.3's example code certainly does err(1,
> "getline"); after free()...)

free(3) doesn't set errno.  At least, not portably.  I definitely wouldn't
worry about the possibility of there being a malloc implementation that
clobbers errno.

The ordering (check or free first) doesn't matter in this case because you
aren't returning from the function early if you have an error on listfp,
but I think the intent of the example in the manpage is to prevent leaks
in such cases, e.g.:

if (ferror(listfp)) {
warn("getline");
return 1;
}
free(line); /* leak */

I would just stick to the manpage example in this case and keep the free(3)
as close as possible to the allocation (getline).

More comments inline.

> 
> diff --git a/bin/md5/md5.c b/bin/md5/md5.c
> index 84adbcf0989..11345bcaa2a 100644
> --- a/bin/md5/md5.c
> +++ b/bin/md5/md5.c
> @@ -549,11 +549,11 @@ digest_filelist(const char *file, struct hash_function 
> *defhash, int selcount,
>   int found, base64, error, cmp, i;
>   size_t algorithm_max, algorithm_min;
>   const char *algorithm;
> - char *filename, *checksum, *buf, *p;
> + char *filename, *checksum, *line, *p;
>   char digest[MAX_DIGEST_LEN + 1];
> - char *lbuf = NULL;
> + ssize_t linelen;
>   FILE *listfp, *fp;
> - size_t len, nread;
> + size_t len, linesize, nread;
>   int *sel_found = NULL;
>   u_char data[32 * 1024];
>   union ANY_CTX context;
> @@ -580,20 +580,16 @@ digest_filelist(const char *file, struct hash_function 
> *defhash, int selcount,
>   }
>  
>   error = found = 0;
> - while ((buf = fgetln(listfp, ))) {
> + line = NULL;
> + linesize = 0;
> + while ((linelen = getline(, , listfp)) != -1) {
> + char *tmpline = line;

You're already modifying the stack variable declarations up above, so
declare tmpline alongside the other char pointers.

>   base64 = 0;
> - if (buf[len - 1] == '\n')
> - buf[len - 1] = '\0';
> - else {
> - if ((lbuf = malloc(len + 1)) == NULL)
> - err(1, NULL);
> -
> - (void)memcpy(lbuf, buf, len);
> - lbuf[len] = '\0';
> - buf = lbuf;
> - }
> - while (isspace((unsigned char)*buf))
> - buf++;
> + len = (size_t)linelen;
> + if (line[len - 1] == '\n')
> + line[len - 1] = '\0';
> + while (isspace((unsigned char)*tmpline))
> + tmpline++;
>  
> 

Re: md5: convert from fgetln(3) to getline(3)

2018-08-14 Thread Lauri Tirkkonen
Hi,

On Tue, Aug 14 2018 12:08:54 -0500, Scott Cheloha wrote:
> > +   len = (size_t)linelen;
> > +   if (line[len - 1] == '\n')
> > +   line[len - 1] = '\0';
> 
> You can just use linelen directly and skip the assignment.

I did it this way because this bit later

if (base64)
cmp = strncmp(checksum, digest, len);

could have been reached without assigning len (if the "could be GNU form"
branch is taken). It looks like checksum and digest will always be null
temrinated, but 'len' may be reduced to something less than linelen
(eg. in the case that the line starts with whitespace). Maybe there's
some reason to not use strcmp here?

> > +   while (isspace((unsigned char)*line))
> > +   line++;
> 
> Incrementing the returned pointer works for fgetln(3) because it
> keeps track of its own buffer internally.  But if you try the same
> trick with getline(3) it may try to realloc your invalid pointer.

Ah, of course. Stupid mistake. Fixed below.

> > @@ -725,11 +720,11 @@ digest_filelist(const char *file, struct 
> > hash_function *defhash, int selcount,
> > error = 1;
> > }
> > }
> > +   free(line);
> 
> getline(3) can fail, too, so you need to check listfp for errors after
> freeing line.

So can fgetln(3), but fair enough.

But shouldn't we check *before* calling free to ensure errno is not
clobbered? (although, getline.3's example code certainly does err(1,
"getline"); after free()...)

diff --git a/bin/md5/md5.c b/bin/md5/md5.c
index 84adbcf0989..11345bcaa2a 100644
--- a/bin/md5/md5.c
+++ b/bin/md5/md5.c
@@ -549,11 +549,11 @@ digest_filelist(const char *file, struct hash_function 
*defhash, int selcount,
int found, base64, error, cmp, i;
size_t algorithm_max, algorithm_min;
const char *algorithm;
-   char *filename, *checksum, *buf, *p;
+   char *filename, *checksum, *line, *p;
char digest[MAX_DIGEST_LEN + 1];
-   char *lbuf = NULL;
+   ssize_t linelen;
FILE *listfp, *fp;
-   size_t len, nread;
+   size_t len, linesize, nread;
int *sel_found = NULL;
u_char data[32 * 1024];
union ANY_CTX context;
@@ -580,20 +580,16 @@ digest_filelist(const char *file, struct hash_function 
*defhash, int selcount,
}
 
error = found = 0;
-   while ((buf = fgetln(listfp, ))) {
+   line = NULL;
+   linesize = 0;
+   while ((linelen = getline(, , listfp)) != -1) {
+   char *tmpline = line;
base64 = 0;
-   if (buf[len - 1] == '\n')
-   buf[len - 1] = '\0';
-   else {
-   if ((lbuf = malloc(len + 1)) == NULL)
-   err(1, NULL);
-
-   (void)memcpy(lbuf, buf, len);
-   lbuf[len] = '\0';
-   buf = lbuf;
-   }
-   while (isspace((unsigned char)*buf))
-   buf++;
+   len = (size_t)linelen;
+   if (line[len - 1] == '\n')
+   line[len - 1] = '\0';
+   while (isspace((unsigned char)*tmpline))
+   tmpline++;
 
/*
 * Crack the line into an algorithm, filename, and checksum.
@@ -603,11 +599,11 @@ digest_filelist(const char *file, struct hash_function 
*defhash, int selcount,
 * Fallback on GNU form:
 *  CHECKSUM  FILENAME
 */
-   p = strchr(buf, ' ');
+   p = strchr(tmpline, ' ');
if (p != NULL && *(p + 1) == '(') {
/* BSD form */
*p = '\0';
-   algorithm = buf;
+   algorithm = tmpline;
len = strlen(algorithm);
if (len > algorithm_max || len < algorithm_min)
continue;
@@ -658,7 +654,7 @@ digest_filelist(const char *file, struct hash_function 
*defhash, int selcount,
if ((hf = defhash) == NULL)
continue;
algorithm = hf->name;
-   checksum = buf;
+   checksum = tmpline;
if ((p = strchr(checksum, ' ')) == NULL)
continue;
if (hf->style == STYLE_CKSUM) {
@@ -725,11 +721,13 @@ digest_filelist(const char *file, struct hash_function 
*defhash, int selcount,
error = 1;
}
}
+   if (ferror(listfp))
+   warn("%s: getline", file);
+   free(line);
if (listfp != stdin)
fclose(listfp);
if (!found)
warnx("%s: no properly formatted checksum lines found", file);
-   free(lbuf);
if (sel_found != NULL) {
/*
   

Re: md5: convert from fgetln(3) to getline(3)

2018-08-14 Thread Scott Cheloha
On Tue, Aug 14, 2018 at 03:34:53PM +0300, Lauri Tirkkonen wrote:
> Similar to previous diffs for join(1) and paste(1). No intended
> functional change.

I agree with the direction.  Comments inline.

> diff --git a/bin/md5/md5.c b/bin/md5/md5.c
> index 84adbcf0989..a8f18262a92 100644
> --- a/bin/md5/md5.c
> +++ b/bin/md5/md5.c
> @@ -549,11 +549,11 @@ digest_filelist(const char *file, struct hash_function 
> *defhash, int selcount,
>   int found, base64, error, cmp, i;
>   size_t algorithm_max, algorithm_min;
>   const char *algorithm;
> - char *filename, *checksum, *buf, *p;
> + char *filename, *checksum, *line, *p;
>   char digest[MAX_DIGEST_LEN + 1];
> - char *lbuf = NULL;
> + ssize_t linelen;
>   FILE *listfp, *fp;
> - size_t len, nread;
> + size_t len, linesize, nread;
>   int *sel_found = NULL;
>   u_char data[32 * 1024];
>   union ANY_CTX context;
> @@ -580,20 +580,15 @@ digest_filelist(const char *file, struct hash_function 
> *defhash, int selcount,
>   }
>  
>   error = found = 0;
> - while ((buf = fgetln(listfp, ))) {
> + line = NULL;
> + linesize = 0;
> + while ((linelen = getline(, , listfp)) != -1) {
>   base64 = 0;
> - if (buf[len - 1] == '\n')
> - buf[len - 1] = '\0';
> - else {
> - if ((lbuf = malloc(len + 1)) == NULL)
> - err(1, NULL);
> -
> - (void)memcpy(lbuf, buf, len);
> - lbuf[len] = '\0';
> - buf = lbuf;
> - }
> - while (isspace((unsigned char)*buf))
> - buf++;
> + len = (size_t)linelen;
> + if (line[len - 1] == '\n')
> + line[len - 1] = '\0';

You can just use linelen directly and skip the assignment.

> + while (isspace((unsigned char)*line))
> + line++;

Incrementing the returned pointer works for fgetln(3) because it
keeps track of its own buffer internally.  But if you try the same
trick with getline(3) it may try to realloc your invalid pointer.

To see this in action, make a cooked checklist file like this:

echo -n ' ' > checklist
md5 /bin/md5 >> checklist
perl -e 'print " "x256' >> checklist
md5 /bin/md5 >> checklist

and then rerun your patched md5 against the checklist with a
stricter malloc.conf(5):

MALLOC_OPTIONS=S md5 -C checklist /bin/md5 /bin/md5

Here, I'm seeing:

(MD5) /bin/md5: OK
md5(11805) in recallocarray(): modified chunk-pointer 0x1026a5f64581
Abort trap (core dumped)

The fix is easy though.  Introduce an additional variable to keep track
of the first non-whitespace character instead of using line.

>  
>   /*
>* Crack the line into an algorithm, filename, and checksum.
> @@ -603,11 +598,11 @@ digest_filelist(const char *file, struct hash_function 
> *defhash, int selcount,
>* Fallback on GNU form:
>*  CHECKSUM  FILENAME
>*/
> - p = strchr(buf, ' ');
> + p = strchr(line, ' ');
>   if (p != NULL && *(p + 1) == '(') {
>   /* BSD form */
>   *p = '\0';
> - algorithm = buf;
> + algorithm = line;
>   len = strlen(algorithm);
>   if (len > algorithm_max || len < algorithm_min)
>   continue;
> @@ -658,7 +653,7 @@ digest_filelist(const char *file, struct hash_function 
> *defhash, int selcount,
>   if ((hf = defhash) == NULL)
>   continue;
>   algorithm = hf->name;
> - checksum = buf;
> + checksum = line;
>   if ((p = strchr(checksum, ' ')) == NULL)
>   continue;
>   if (hf->style == STYLE_CKSUM) {
> @@ -725,11 +720,11 @@ digest_filelist(const char *file, struct hash_function 
> *defhash, int selcount,
>   error = 1;
>   }
>   }
> + free(line);

getline(3) can fail, too, so you need to check listfp for errors after
freeing line.

>   if (listfp != stdin)
>   fclose(listfp);
>   if (!found)
>   warnx("%s: no properly formatted checksum lines found", file);
> - free(lbuf);
>   if (sel_found != NULL) {
>   /*
>* Mark found files by setting them to NULL so that we can
> 
> -- 
> Lauri Tirkkonen | lotheac @ IRCnet
>