lib: convert Curl_get_line to use dynbuf

Create the line in a dynbuf. Aborts the reading of the file on
errors. Avoids having to always allocate maximum amount from the
start. Avoids direct malloc.

Closes #12846
This commit is contained in:
Daniel Stenberg 2024-02-06 10:15:52 +01:00
parent 8f40b30c31
commit 142ac257b3
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
7 changed files with 96 additions and 120 deletions

View File

@ -209,7 +209,6 @@ static CURLcode altsvc_add(struct altsvcinfo *asi, char *line)
static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file) static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file)
{ {
CURLcode result = CURLE_OK; CURLcode result = CURLE_OK;
char *line = NULL;
FILE *fp; FILE *fp;
/* we need a private copy of the file name so that the altsvc cache file /* we need a private copy of the file name so that the altsvc cache file
@ -221,11 +220,10 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file)
fp = fopen(file, FOPEN_READTEXT); fp = fopen(file, FOPEN_READTEXT);
if(fp) { if(fp) {
line = malloc(MAX_ALTSVC_LINE); struct dynbuf buf;
if(!line) Curl_dyn_init(&buf, MAX_ALTSVC_LINE);
goto fail; while(Curl_get_line(&buf, fp)) {
while(Curl_get_line(line, MAX_ALTSVC_LINE, fp)) { char *lineptr = Curl_dyn_ptr(&buf);
char *lineptr = line;
while(*lineptr && ISBLANK(*lineptr)) while(*lineptr && ISBLANK(*lineptr))
lineptr++; lineptr++;
if(*lineptr == '#') if(*lineptr == '#')
@ -234,16 +232,10 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file)
altsvc_add(asi, lineptr); altsvc_add(asi, lineptr);
} }
free(line); /* free the line buffer */ Curl_dyn_free(&buf); /* free the line buffer */
fclose(fp); fclose(fp);
} }
return result; return result;
fail:
Curl_safefree(asi->filename);
free(line);
fclose(fp);
return CURLE_OUT_OF_MEMORY;
} }
/* /*

View File

@ -1205,7 +1205,6 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
bool newsession) bool newsession)
{ {
struct CookieInfo *c; struct CookieInfo *c;
char *line = NULL;
FILE *handle = NULL; FILE *handle = NULL;
if(!inc) { if(!inc) {
@ -1241,16 +1240,14 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
c->running = FALSE; /* this is not running, this is init */ c->running = FALSE; /* this is not running, this is init */
if(fp) { if(fp) {
struct dynbuf buf;
line = malloc(MAX_COOKIE_LINE); Curl_dyn_init(&buf, MAX_COOKIE_LINE);
if(!line) while(Curl_get_line(&buf, fp)) {
goto fail; char *lineptr = Curl_dyn_ptr(&buf);
while(Curl_get_line(line, MAX_COOKIE_LINE, fp)) {
char *lineptr = line;
bool headerline = FALSE; bool headerline = FALSE;
if(checkprefix("Set-Cookie:", line)) { if(checkprefix("Set-Cookie:", lineptr)) {
/* This is a cookie line, get it! */ /* This is a cookie line, get it! */
lineptr = &line[11]; lineptr += 11;
headerline = TRUE; headerline = TRUE;
while(*lineptr && ISBLANK(*lineptr)) while(*lineptr && ISBLANK(*lineptr))
lineptr++; lineptr++;
@ -1258,7 +1255,7 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE); Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE);
} }
free(line); /* free the line buffer */ Curl_dyn_free(&buf); /* free the line buffer */
/* /*
* Remove expired cookies from the hash. We must make sure to run this * Remove expired cookies from the hash. We must make sure to run this
@ -1274,18 +1271,6 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
c->running = TRUE; /* now, we're running */ c->running = TRUE; /* now, we're running */
return c; return c;
fail:
free(line);
/*
* Only clean up if we allocated it here, as the original could still be in
* use by a share handle.
*/
if(!inc)
Curl_cookie_cleanup(c);
if(handle)
fclose(handle);
return NULL; /* out of memory */
} }
/* /*

View File

@ -33,14 +33,16 @@
#include "memdebug.h" #include "memdebug.h"
/* /*
* Curl_get_line() makes sure to only return complete whole lines that fit in * Curl_get_line() makes sure to only return complete whole lines that end
* 'len' bytes and end with a newline. * newlines.
*/ */
char *Curl_get_line(char *buf, int len, FILE *input) int Curl_get_line(struct dynbuf *buf, FILE *input)
{ {
bool partial = FALSE; CURLcode result;
char buffer[128];
Curl_dyn_reset(buf);
while(1) { while(1) {
char *b = fgets(buf, len, input); char *b = fgets(buffer, sizeof(buffer), input);
if(b) { if(b) {
size_t rlen = strlen(b); size_t rlen = strlen(b);
@ -48,39 +50,28 @@ char *Curl_get_line(char *buf, int len, FILE *input)
if(!rlen) if(!rlen)
break; break;
if(b[rlen-1] == '\n') { result = Curl_dyn_addn(buf, b, rlen);
/* b is \n terminated */ if(result)
if(partial) { /* too long line or out of memory */
partial = FALSE; return 0; /* error */
continue;
}
return b;
}
else if(feof(input)) {
if(partial)
/* Line is already too large to return, ignore rest */
break;
if(rlen + 1 < (size_t) len) { else if(b[rlen-1] == '\n')
/* b is EOF terminated, insert missing \n */ /* end of the line */
b[rlen] = '\n'; return 1; /* all good */
b[rlen + 1] = '\0';
return b; else if(feof(input)) {
} /* append a newline */
else result = Curl_dyn_addn(buf, "\n", 1);
/* Maximum buffersize reached + EOF if(result)
* This line is impossible to add a \n to so we'll ignore it /* too long line or out of memory */
*/ return 0; /* error */
break; return 1; /* all good */
} }
else
/* Maximum buffersize reached */
partial = TRUE;
} }
else else
break; break;
} }
return NULL; return 0;
} }
#endif /* if not disabled */ #endif /* if not disabled */

View File

@ -24,8 +24,9 @@
* *
***************************************************************************/ ***************************************************************************/
/* get_line() makes sure to only return complete whole lines that fit in 'len' #include "dynbuf.h"
* bytes and end with a newline. */
char *Curl_get_line(char *buf, int len, FILE *input); /* Curl_get_line() returns complete lines that end with a newline. */
int Curl_get_line(struct dynbuf *buf, FILE *input);
#endif /* HEADER_CURL_GET_LINE_H */ #endif /* HEADER_CURL_GET_LINE_H */

View File

@ -511,7 +511,6 @@ static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h)
static CURLcode hsts_load(struct hsts *h, const char *file) static CURLcode hsts_load(struct hsts *h, const char *file)
{ {
CURLcode result = CURLE_OK; CURLcode result = CURLE_OK;
char *line = NULL;
FILE *fp; FILE *fp;
/* we need a private copy of the file name so that the hsts cache file /* we need a private copy of the file name so that the hsts cache file
@ -523,11 +522,10 @@ static CURLcode hsts_load(struct hsts *h, const char *file)
fp = fopen(file, FOPEN_READTEXT); fp = fopen(file, FOPEN_READTEXT);
if(fp) { if(fp) {
line = malloc(MAX_HSTS_LINE); struct dynbuf buf;
if(!line) Curl_dyn_init(&buf, MAX_HSTS_LINE);
goto fail; while(Curl_get_line(&buf, fp)) {
while(Curl_get_line(line, MAX_HSTS_LINE, fp)) { char *lineptr = Curl_dyn_ptr(&buf);
char *lineptr = line;
while(*lineptr && ISBLANK(*lineptr)) while(*lineptr && ISBLANK(*lineptr))
lineptr++; lineptr++;
if(*lineptr == '#') if(*lineptr == '#')
@ -536,15 +534,10 @@ static CURLcode hsts_load(struct hsts *h, const char *file)
hsts_add(h, lineptr); hsts_add(h, lineptr);
} }
free(line); /* free the line buffer */ Curl_dyn_free(&buf); /* free the line buffer */
fclose(fp); fclose(fp);
} }
return result; return result;
fail:
Curl_safefree(h->filename);
fclose(fp);
return CURLE_OUT_OF_MEMORY;
} }
/* /*

View File

@ -53,6 +53,8 @@ enum host_lookup_state {
#define NETRC_FAILED -1 #define NETRC_FAILED -1
#define NETRC_SUCCESS 0 #define NETRC_SUCCESS 0
#define MAX_NETRC_LINE 4096
/* /*
* Returns zero on success. * Returns zero on success.
*/ */
@ -80,13 +82,14 @@ static int parsenetrc(const char *host,
file = fopen(netrcfile, FOPEN_READTEXT); file = fopen(netrcfile, FOPEN_READTEXT);
if(file) { if(file) {
bool done = FALSE; bool done = FALSE;
char netrcbuffer[4096]; struct dynbuf buf;
int netrcbuffsize = (int)sizeof(netrcbuffer); Curl_dyn_init(&buf, MAX_NETRC_LINE);
while(!done && Curl_get_line(netrcbuffer, netrcbuffsize, file)) { while(!done && Curl_get_line(&buf, file)) {
char *tok; char *tok;
char *tok_end; char *tok_end;
bool quoted; bool quoted;
char *netrcbuffer = Curl_dyn_ptr(&buf);
if(state == MACDEF) { if(state == MACDEF) {
if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r')) if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r'))
state = NOTHING; state = NOTHING;
@ -245,6 +248,7 @@ static int parsenetrc(const char *host,
} /* while Curl_get_line() */ } /* while Curl_get_line() */
out: out:
Curl_dyn_free(&buf);
if(!retcode) { if(!retcode) {
/* success */ /* success */
if(login_alloc) { if(login_alloc) {

View File

@ -69,7 +69,7 @@ static const char *filecontents[] = {
"LINE1\n" "LINE1\n"
C4096 "SOME EXTRA TEXT", C4096 "SOME EXTRA TEXT",
/* First and third line should be read */ /* Only first should be read */
"LINE1\n" "LINE1\n"
C4096 "SOME EXTRA TEXT\n" C4096 "SOME EXTRA TEXT\n"
"LINE3\n", "LINE3\n",
@ -84,11 +84,13 @@ static const char *filecontents[] = {
UNITTEST_START UNITTEST_START
size_t i; size_t i;
int rc = 0;
for(i = 0; i < NUMTESTS; i++) { for(i = 0; i < NUMTESTS; i++) {
FILE *fp; FILE *fp;
char buf[4096]; struct dynbuf buf;
int len = 4096; int len = 4096;
char *line; char *line;
Curl_dyn_init(&buf, len);
fp = fopen(arg, "wb"); fp = fopen(arg, "wb");
abort_unless(fp != NULL, "Cannot open testfile"); abort_unless(fp != NULL, "Cannot open testfile");
@ -101,65 +103,73 @@ UNITTEST_START
fprintf(stderr, "Test %zd...", i); fprintf(stderr, "Test %zd...", i);
switch(i) { switch(i) {
case 0: case 0:
line = Curl_get_line(buf, len, fp); rc = Curl_get_line(&buf, fp);
line = Curl_dyn_ptr(&buf);
fail_unless(line && !strcmp("LINE1\n", line), fail_unless(line && !strcmp("LINE1\n", line),
"First line failed (1)"); "First line failed (1)");
line = Curl_get_line(buf, len, fp); rc = Curl_get_line(&buf, fp);
line = Curl_dyn_ptr(&buf);
fail_unless(line && !strcmp("LINE2 NEWLINE\n", line), fail_unless(line && !strcmp("LINE2 NEWLINE\n", line),
"Second line failed (1)"); "Second line failed (1)");
line = Curl_get_line(buf, len, fp); rc = Curl_get_line(&buf, fp);
abort_unless(line == NULL, "Missed EOF (1)"); abort_unless(!Curl_dyn_len(&buf), "Missed EOF (1)");
break; break;
case 1: case 1:
line = Curl_get_line(buf, len, fp); rc = Curl_get_line(&buf, fp);
line = Curl_dyn_ptr(&buf);
fail_unless(line && !strcmp("LINE1\n", line), fail_unless(line && !strcmp("LINE1\n", line),
"First line failed (2)"); "First line failed (2)");
line = Curl_get_line(buf, len, fp); rc = Curl_get_line(&buf, fp);
line = Curl_dyn_ptr(&buf);
fail_unless(line && !strcmp("LINE2 NONEWLINE\n", line), fail_unless(line && !strcmp("LINE2 NONEWLINE\n", line),
"Second line failed (2)"); "Second line failed (2)");
line = Curl_get_line(buf, len, fp); rc = Curl_get_line(&buf, fp);
abort_unless(line == NULL, "Missed EOF (2)"); abort_unless(!Curl_dyn_len(&buf), "Missed EOF (2)");
break; break;
case 2: case 2:
line = Curl_get_line(buf, len, fp); rc = Curl_get_line(&buf, fp);
line = Curl_dyn_ptr(&buf);
fail_unless(line && !strcmp("LINE1\n", line), fail_unless(line && !strcmp("LINE1\n", line),
"First line failed (3)"); "First line failed (3)");
line = Curl_get_line(buf, len, fp); rc = Curl_get_line(&buf, fp);
fail_unless(line == NULL, fail_unless(!Curl_dyn_len(&buf),
"Did not detect max read on EOF (3)"); "Did not detect max read on EOF (3)");
break; break;
case 3: case 3:
line = Curl_get_line(buf, len, fp); rc = Curl_get_line(&buf, fp);
line = Curl_dyn_ptr(&buf);
fail_unless(line && !strcmp("LINE1\n", line), fail_unless(line && !strcmp("LINE1\n", line),
"First line failed (4)"); "First line failed (4)");
line = Curl_get_line(buf, len, fp); rc = Curl_get_line(&buf, fp);
fail_unless(line == NULL, fail_unless(!Curl_dyn_len(&buf),
"Did not ignore partial on EOF (4)"); "Did not ignore partial on EOF (4)");
break; break;
case 4: case 4:
line = Curl_get_line(buf, len, fp); rc = Curl_get_line(&buf, fp);
line = Curl_dyn_ptr(&buf);
fail_unless(line && !strcmp("LINE1\n", line), fail_unless(line && !strcmp("LINE1\n", line),
"First line failed (5)"); "First line failed (5)");
line = Curl_get_line(buf, len, fp); rc = Curl_get_line(&buf, fp);
fail_unless(line && !strcmp("LINE3\n", line), fail_unless(!Curl_dyn_len(&buf),
"Third line failed (5)"); "Did not bail out on too long line");
line = Curl_get_line(buf, len, fp);
abort_unless(line == NULL, "Missed EOF (5)");
break; break;
case 5: case 5:
line = Curl_get_line(buf, len, fp); rc = Curl_get_line(&buf, fp);
line = Curl_dyn_ptr(&buf);
fail_unless(line && !strcmp("LINE1\x1aTEST\n", line), fail_unless(line && !strcmp("LINE1\x1aTEST\n", line),
"Missed/Misinterpreted ^Z (6)"); "Missed/Misinterpreted ^Z (6)");
line = Curl_get_line(buf, len, fp); rc = Curl_get_line(&buf, fp);
abort_unless(line == NULL, "Missed EOF (6)"); abort_unless(!Curl_dyn_len(&buf), "Missed EOF (6)");
break; break;
default: default:
abort_unless(1, "Unknown case"); abort_unless(1, "Unknown case");
break; break;
} }
Curl_dyn_free(&buf);
fclose(fp); fclose(fp);
fprintf(stderr, "OK\n"); fprintf(stderr, "OK\n");
} }
return rc;
UNITTEST_STOP UNITTEST_STOP
#ifdef __GNUC__ #ifdef __GNUC__