Bug 192701 - man page for getline(3) needs safer output handling in EXAMPLE
Summary: man page for getline(3) needs safer output handling in EXAMPLE
Status: RESOLVED CODE_FIX
Alias: None
Product: Documentation
Classification: Unclassified
Component: man-pages (show other bugs)
Hardware: All Linux
: P1 normal
Assignee: documentation_man-pages@kernel-bugs.osdl.org
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2017-01-16 21:39 UTC by Kent Fredic
Modified: 2017-01-23 02:44 UTC (History)
1 user (show)

See Also:
Kernel Version:
Subsystem:
Regression: No
Bisected commit-id:


Attachments

Description Kent Fredic 2017-01-16 21:39:18 UTC
The example input/output handler in this EXAMPLE is subject to introducing subtle bugs if the input stream contains literal null bytes.

Subsequently, there should be some warning that this occurs, or an alternative using fwrite(3) might be better. 

( I also note there's no obvious direction in the printf(3) pages what one should do if they expect their data to contain nulls, but clarifying that is much harder in such an already lengthy document )


Context:
- Relative C novice, have experience, but dated and in need of refresh
- Aware there are fun security concerns in C, but not fully able to remember them all
- Wanted to use a "Safe" way to handle input linewise, internet searches lead me to getline
- Made implementation of a simple thing I wanted ( purpose was to simply skip a fixed number of lines from input before writing them to output )
- Basically copied example with minor adjustments
- Compared output of chunks of /dev/urandom with my program with a perl oneliner that did the same
- Anomalous problems happened.
- Output analysis with `xxd` showed me \0 
- *remembers how strings in C work*
- Have to work out how to output strings with nulls
- Eventually discover fwrite() 

Granted a more seasoned C programmer would have likely not made this mistake, but I consider it an implementation bug in the example as-is that it silently mutates data.

---
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>

int
main(void)
{
   FILE *stream = stdin;
   char *line = NULL;
   size_t len = 0;
   ssize_t read;

   if (stream == NULL)
       exit(EXIT_FAILURE);

   while ((read = getline(&line, &len, stream)) != -1) {
       printf("Retrieved line of length %zu :\n", read);
       printf("%s", line);
   }

   free(line);
   fclose(stream);
   exit(EXIT_SUCCESS);
}


---
echo -e 'hello\0world\nthis is a test\n' | /tmp/getline 

Retrieved line of length 12 :
helloRetrieved line of length 15 :
this is a test
Retrieved line of length 1 :
---


> This  page  is  part of release 4.09 of the Linux man-pages project
Comment 1 Michael Kerrisk 2017-01-23 02:44:05 UTC
I've modified the example to use fwrite(3) (and also made a few other fixes to the program). Closing this report.


       #define _GNU_SOURCE
       #include <stdio.h>
       #include <stdlib.h>

       int
       main(int argc, char *argv[])
       {
           FILE *stream;
           char *line = NULL;
           size_t len = 0;
           ssize_t nread;

           if (argc != 2) {
               fprintf(stderr, "Usage: %s <file>\n", argv[1]);
               exit(EXIT_FAILURE);
           }

           stream = fopen(argv[1], "r");
           if (stream == NULL) {
               perror("fopen");
               exit(EXIT_FAILURE);
           }

           while ((nread = getline(&line, &len, stream)) != -1) {
               printf("Retrieved line of length %zu:\n", nread);
               fwrite(line, nread, 1, stdout);
           }

           free(line);
           fclose(stream);
           exit(EXIT_SUCCESS);
       }

Note You need to log in before you can comment on or make changes to this bug.