Skip to content

Commit fd7d7b7

Browse files
committed
Improve checks for GUC recovery_target_timeline
Currently check_recovery_target_timeline() converts any value that is not "current", "latest", or a valid integer to 0. So, for example, the following configuration added to postgresql.conf followed by a startup: recovery_target_timeline = 'bogus' recovery_target_timeline = '9999999999' ... results in the following error patterns: FATAL: 22023: recovery target timeline 0 does not exist FATAL: 22023: recovery target timeline 1410065407 does not exist This is confusing, because the server does not reflect the intention of the user, and just reports incorrect data unrelated to the GUC. The origin of the problem is that we do not perform a range check in the GUC value passed-in for recovery_target_timeline. This commit improves the situation by using strtou64() and by providing stricter range checks. Some test cases are added for the cases of an incorrect, an upper-bound and a lower-bound timeline value, checking the sanity of the reports based on the contents of the server logs. Author: David Steele <david@pgmasters.net> Discussion: https://postgr.es/m/e5d472c7-e9be-4710-8dc4-ebe721b62cea@pgbackrest.org
1 parent 0da29e4 commit fd7d7b7

File tree

2 files changed

+65
-3
lines changed

2 files changed

+65
-3
lines changed

src/backend/access/transam/xlogrecovery.c

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4994,13 +4994,25 @@ check_recovery_target_timeline(char **newval, void **extra, GucSource source)
49944994
rttg = RECOVERY_TARGET_TIMELINE_LATEST;
49954995
else
49964996
{
4997+
char *endp;
4998+
uint64 timeline;
4999+
49975000
rttg = RECOVERY_TARGET_TIMELINE_NUMERIC;
49985001

49995002
errno = 0;
5000-
strtoul(*newval, NULL, 0);
5001-
if (errno == EINVAL || errno == ERANGE)
5003+
timeline = strtou64(*newval, &endp, 0);
5004+
5005+
if (*endp != '\0' || errno == EINVAL || errno == ERANGE)
5006+
{
5007+
GUC_check_errdetail("\"%s\" is not a valid number.",
5008+
"recovery_target_timeline");
5009+
return false;
5010+
}
5011+
5012+
if (timeline < 1 || timeline > PG_UINT32_MAX)
50025013
{
5003-
GUC_check_errdetail("\"recovery_target_timeline\" is not a valid number.");
5014+
GUC_check_errdetail("\"%s\" must be between %u and %u.",
5015+
"recovery_target_timeline", 1, UINT_MAX);
50045016
return false;
50055017
}
50065018
}

src/test/recovery/t/003_recovery_targets.pl

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,4 +187,54 @@ sub test_recovery_standby
187187
qr/FATAL: .* recovery ended before configured recovery target was reached/,
188188
'recovery end before target reached is a fatal error');
189189

190+
# Invalid timeline target
191+
$node_standby = PostgreSQL::Test::Cluster->new('standby_9');
192+
$node_standby->init_from_backup($node_primary, 'my_backup',
193+
has_restoring => 1);
194+
$node_standby->append_conf('postgresql.conf',
195+
"recovery_target_timeline = 'bogus'");
196+
197+
$res = run_log(
198+
[
199+
'pg_ctl',
200+
'--pgdata' => $node_standby->data_dir,
201+
'--log' => $node_standby->logfile,
202+
'start',
203+
]);
204+
ok(!$res, 'invalid timeline target (bogus value)');
205+
206+
my $log_start = $node_standby->wait_for_log("is not a valid number");
207+
208+
# Timeline target out of min range
209+
$node_standby->append_conf('postgresql.conf',
210+
"recovery_target_timeline = '0'");
211+
212+
$res = run_log(
213+
[
214+
'pg_ctl',
215+
'--pgdata' => $node_standby->data_dir,
216+
'--log' => $node_standby->logfile,
217+
'start',
218+
]);
219+
ok(!$res, 'invalid timeline target (lower bound check)');
220+
221+
$log_start =
222+
$node_standby->wait_for_log("must be between 1 and 4294967295", $log_start);
223+
224+
# Timeline target out of max range
225+
$node_standby->append_conf('postgresql.conf',
226+
"recovery_target_timeline = '4294967296'");
227+
228+
$res = run_log(
229+
[
230+
'pg_ctl',
231+
'--pgdata' => $node_standby->data_dir,
232+
'--log' => $node_standby->logfile,
233+
'start',
234+
]);
235+
ok(!$res, 'invalid timeline target (upper bound check)');
236+
237+
$log_start =
238+
$node_standby->wait_for_log("must be between 1 and 4294967295", $log_start);
239+
190240
done_testing();

0 commit comments

Comments
 (0)