> Index: gen/getusershell.c
> ===================================================================
> RCS file: /cvs/src/lib/libc/gen/getusershell.c,v
> retrieving revision 1.16
> diff -u -p -r1.16 getusershell.c
> --- gen/getusershell.c 14 Sep 2015 16:09:13 -0000 1.16
> +++ gen/getusershell.c 5 Dec 2015 16:08:56 -0000
> @@ -84,48 +86,52 @@ setusershell(void)
> static char **
> initshells(void)
> {
> - char **sp, *cp;
> + char buf[PATH_MAX];
> + int nshells = 0, nalloc;
I would prefer size_t for nshells and nalloc.
> + char *cp;
> FILE *fp;
> - struct stat statb;
>
> free(shells);
> shells = NULL;
> - free(strings);
> - strings = NULL;
> +
> if ((fp = fopen(_PATH_SHELLS, "re")) == NULL)
> return (okshells);
> - if (fstat(fileno(fp), &statb) == -1) {
> - (void)fclose(fp);
> - return (okshells);
> - }
> - if (statb.st_size > SIZE_MAX) {
> - (void)fclose(fp);
> - return (okshells);
> - }
> - if ((strings = malloc((size_t)statb.st_size)) == NULL) {
> - (void)fclose(fp);
> - return (okshells);
> - }
> - shells = calloc((size_t)(statb.st_size / 3 + 2), sizeof (char *));
> - if (shells == NULL) {
> - (void)fclose(fp);
> - free(strings);
> - strings = NULL;
> - return (okshells);
> - }
> - sp = shells;
> - cp = strings;
> - while (fgets(cp, PATH_MAX + 1, fp) != NULL) {
> +
> + nalloc = 10; // just an initial guess
> + nshells = 0;
> + shells = reallocarray(NULL, nalloc, sizeof (char *));
> + if (shells == NULL)
> + goto fail;
> + while ((cp = fgets(buf, sizeof(buf), fp)) != NULL) {
We already have to dynamically allocate memory anyway, so getline() would
fix some issues we could face while parsing files. The buffer is PATH_MAX
bytes long, which should be sufficient, but if a comment is PATH_MAX + x
bytes in size, we would parse the "x" part as a real path due to fgets'
truncation/wrapping.
> while (*cp != '#' && *cp != '/' && *cp != '\0')
> cp++;
> if (*cp == '#' || *cp == '\0')
> continue;
> - *sp++ = cp;
> + if (!(shells[nshells] = strdup(cp)))
> + goto fail;
> + cp = shells[nshells];
> while (!isspace((unsigned char)*cp) && *cp != '#' && *cp !=
> '\0')
> cp++;
> *cp++ = '\0';
And I still think that the current code is a bit too permissive in parsing
things. I mean what's the point in allowing lines like:
sometextwithoutspace/bin/ksh should be used for logins # seriously!
Which would result in /bin/ksh, by the way.
Didn't notice the consequences that arise by keeping the descriptor open,
so I'm fine with an alternative approach. Yet we might make the code a
bit easier to review by not allowing such weird lines. What it should
expect and enforce:
- a valid line has to start with a slash
- comments are chopped off
- comments are supposed to be at the beginning of a line
So if somebody writes "/bin/ksh # comment", that actually leads to "/bin/ksh ",
with an additional whitespace at the end. Currently we couldn't even specify a
shell with a whitespace in its path.
Tobias