Skip to content

Commit 4fe2aa7

Browse files
committed
Reduce memory leakage in initdb.
While testing commit 3e51b27, I noted that initdb leaks about a megabyte worth of data due to the sloppy bookkeeping in its string-manipulating code. That's not a huge amount on modern machines, but it's still kind of annoying, and it's easy to fix by recognizing that we might as well treat these arrays of strings as modifiable-in-place. There's no caller that cares about preserving the old state of the array after replace_token or replace_guc_value. With this fix, valgrind sees only a few hundred bytes leaked during an initdb run. Discussion: https://postgr.es/m/2844176.1674681919@sss.pgh.pa.us
1 parent 3e51b27 commit 4fe2aa7

File tree

1 file changed

+34
-53
lines changed

1 file changed

+34
-53
lines changed

src/bin/initdb/initdb.c

Lines changed: 34 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -402,47 +402,35 @@ add_stringlist_item(_stringlist **listhead, const char *str)
402402
}
403403

404404
/*
405-
* Make a copy of the array of lines, with token replaced by replacement
405+
* Modify the array of lines, replacing "token" by "replacement"
406406
* the first time it occurs on each line.
407407
*
408-
* The original data structure is not changed, but we share any unchanged
409-
* strings with it. (This definition lends itself to memory leaks, but
410-
* we don't care too much about leaks in this program.)
408+
* The array must be a malloc'd array of individually malloc'd strings.
409+
* We free any discarded strings.
411410
*
412411
* This does most of what sed was used for in the shell script, but
413412
* doesn't need any regexp stuff.
414413
*/
415414
static char **
416415
replace_token(char **lines, const char *token, const char *replacement)
417416
{
418-
int numlines = 1;
419-
int i;
420-
char **result;
421417
int toklen,
422418
replen,
423419
diff;
424420

425-
for (i = 0; lines[i]; i++)
426-
numlines++;
427-
428-
result = (char **) pg_malloc(numlines * sizeof(char *));
429-
430421
toklen = strlen(token);
431422
replen = strlen(replacement);
432423
diff = replen - toklen;
433424

434-
for (i = 0; i < numlines; i++)
425+
for (int i = 0; lines[i]; i++)
435426
{
436427
char *where;
437428
char *newline;
438429
int pre;
439430

440-
/* just copy pointer if NULL or no change needed */
441-
if (lines[i] == NULL || (where = strstr(lines[i], token)) == NULL)
442-
{
443-
result[i] = lines[i];
431+
/* nothing to do if no change needed */
432+
if ((where = strstr(lines[i], token)) == NULL)
444433
continue;
445-
}
446434

447435
/* if we get here a change is needed - set up new line */
448436

@@ -456,14 +444,15 @@ replace_token(char **lines, const char *token, const char *replacement)
456444

457445
strcpy(newline + pre + replen, lines[i] + pre + toklen);
458446

459-
result[i] = newline;
447+
free(lines[i]);
448+
lines[i] = newline;
460449
}
461450

462-
return result;
451+
return lines;
463452
}
464453

465454
/*
466-
* Make a copy of the array of lines, replacing the possibly-commented-out
455+
* Modify the array of lines, replacing the possibly-commented-out
467456
* assignment of parameter guc_name with a live assignment of guc_value.
468457
* The value will be suitably quoted.
469458
*
@@ -474,18 +463,15 @@ replace_token(char **lines, const char *token, const char *replacement)
474463
* We assume there's at most one matching assignment. If we find no match,
475464
* append a new line with the desired assignment.
476465
*
477-
* The original data structure is not changed, but we share any unchanged
478-
* strings with it. (This definition lends itself to memory leaks, but
479-
* we don't care too much about leaks in this program.)
466+
* The array must be a malloc'd array of individually malloc'd strings.
467+
* We free any discarded strings.
480468
*/
481469
static char **
482470
replace_guc_value(char **lines, const char *guc_name, const char *guc_value,
483471
bool mark_as_comment)
484472
{
485-
char **result;
486473
int namelen = strlen(guc_name);
487474
PQExpBuffer newline = createPQExpBuffer();
488-
int numlines = 0;
489475
int i;
490476

491477
/* prepare the replacement line, except for possible comment and newline */
@@ -497,17 +483,7 @@ replace_guc_value(char **lines, const char *guc_name, const char *guc_value,
497483
else
498484
appendPQExpBufferStr(newline, guc_value);
499485

500-
/* create the new pointer array */
501486
for (i = 0; lines[i]; i++)
502-
numlines++;
503-
504-
/* leave room for one extra string in case we need to append */
505-
result = (char **) pg_malloc((numlines + 2) * sizeof(char *));
506-
507-
/* initialize result with all the same strings */
508-
memcpy(result, lines, (numlines + 1) * sizeof(char *));
509-
510-
for (i = 0; i < numlines; i++)
511487
{
512488
const char *where;
513489

@@ -517,7 +493,7 @@ replace_guc_value(char **lines, const char *guc_name, const char *guc_value,
517493
* overrides a previous assignment. We allow leading whitespace too,
518494
* although normally there wouldn't be any.
519495
*/
520-
where = result[i];
496+
where = lines[i];
521497
while (*where == '#' || isspace((unsigned char) *where))
522498
where++;
523499
if (strncmp(where, guc_name, namelen) != 0)
@@ -540,7 +516,7 @@ replace_guc_value(char **lines, const char *guc_name, const char *guc_value,
540516
int oldindent = 0;
541517
int newindent;
542518

543-
for (ptr = result[i]; ptr < where; ptr++)
519+
for (ptr = lines[i]; ptr < where; ptr++)
544520
{
545521
if (*ptr == '\t')
546522
oldindent += 8 - (oldindent % 8);
@@ -573,23 +549,27 @@ replace_guc_value(char **lines, const char *guc_name, const char *guc_value,
573549
else
574550
appendPQExpBufferChar(newline, '\n');
575551

576-
result[i] = newline->data;
552+
free(lines[i]);
553+
lines[i] = newline->data;
577554

578555
break; /* assume there's only one match */
579556
}
580557

581-
if (i >= numlines)
558+
if (lines[i] == NULL)
582559
{
583560
/*
584561
* No match, so append a new entry. (We rely on the bootstrap server
585562
* to complain if it's not a valid GUC name.)
586563
*/
587564
appendPQExpBufferChar(newline, '\n');
588-
result[numlines++] = newline->data;
589-
result[numlines] = NULL; /* keep the array null-terminated */
565+
lines = pg_realloc_array(lines, char *, i + 2);
566+
lines[i++] = newline->data;
567+
lines[i] = NULL; /* keep the array null-terminated */
590568
}
591569

592-
return result;
570+
free(newline); /* but don't free newline->data */
571+
572+
return lines;
593573
}
594574

595575
/*
@@ -626,6 +606,8 @@ guc_value_requires_quotes(const char *guc_value)
626606

627607
/*
628608
* get the lines from a text file
609+
*
610+
* The result is a malloc'd array of individually malloc'd strings.
629611
*/
630612
static char **
631613
readfile(const char *path)
@@ -668,6 +650,9 @@ readfile(const char *path)
668650
/*
669651
* write an array of lines to a file
670652
*
653+
* "lines" must be a malloc'd array of individually malloc'd strings.
654+
* All that data is freed here.
655+
*
671656
* This is only used to write text files. Use fopen "w" not PG_BINARY_W
672657
* so that the resulting configuration files are nicely editable on Windows.
673658
*/
@@ -687,6 +672,7 @@ writefile(char *path, char **lines)
687672
}
688673
if (fclose(out_file))
689674
pg_fatal("could not close file \"%s\": %m", path);
675+
free(lines);
690676
}
691677

692678
/*
@@ -1218,7 +1204,6 @@ setup_config(void)
12181204
char **conflines;
12191205
char repltok[MAXPGPATH];
12201206
char path[MAXPGPATH];
1221-
char *autoconflines[3];
12221207
_stringlist *gnames,
12231208
*gvalues;
12241209

@@ -1384,18 +1369,17 @@ setup_config(void)
13841369
if (chmod(path, pg_file_create_mode) != 0)
13851370
pg_fatal("could not change permissions of \"%s\": %m", path);
13861371

1387-
free(conflines);
1388-
13891372

13901373
/* postgresql.auto.conf */
13911374

1392-
autoconflines[0] = pg_strdup("# Do not edit this file manually!\n");
1393-
autoconflines[1] = pg_strdup("# It will be overwritten by the ALTER SYSTEM command.\n");
1394-
autoconflines[2] = NULL;
1375+
conflines = pg_malloc_array(char *, 3);
1376+
conflines[0] = pg_strdup("# Do not edit this file manually!\n");
1377+
conflines[1] = pg_strdup("# It will be overwritten by the ALTER SYSTEM command.\n");
1378+
conflines[2] = NULL;
13951379

13961380
sprintf(path, "%s/postgresql.auto.conf", pg_data);
13971381

1398-
writefile(path, autoconflines);
1382+
writefile(path, conflines);
13991383
if (chmod(path, pg_file_create_mode) != 0)
14001384
pg_fatal("could not change permissions of \"%s\": %m", path);
14011385

@@ -1466,7 +1450,6 @@ setup_config(void)
14661450
if (chmod(path, pg_file_create_mode) != 0)
14671451
pg_fatal("could not change permissions of \"%s\": %m", path);
14681452

1469-
free(conflines);
14701453

14711454
/* pg_ident.conf */
14721455

@@ -1478,8 +1461,6 @@ setup_config(void)
14781461
if (chmod(path, pg_file_create_mode) != 0)
14791462
pg_fatal("could not change permissions of \"%s\": %m", path);
14801463

1481-
free(conflines);
1482-
14831464
check_ok();
14841465
}
14851466

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy