Skip to content

Commit c7e7dc5

Browse files
committed
Xlog encryption bugfix: offset calculation was off on TLI change
The min/max comparisons of LSNs assumed that everyting is in the same timeline. In practice, with replication + recovery combinations, it is possible that keys span at least 3 timelines, which means that this has to be included in both combinations, as in other timelines, the restrictions are less strict.
1 parent 6ddc86c commit c7e7dc5

File tree

1 file changed

+42
-4
lines changed

1 file changed

+42
-4
lines changed

contrib/pg_tde/src/access/pg_tde_xlog_smgr.c

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -415,16 +415,54 @@ TDEXLogCryptBuffer(void *buf, size_t count, off_t offset,
415415
if (wal_location_cmp(data_start, curr_key->end) < 0 && wal_location_cmp(data_end, curr_key->start) > 0)
416416
{
417417
char iv_prefix[16];
418-
off_t dec_off = XLogSegmentOffset(Max(data_start.lsn, curr_key->start.lsn), segSize);
419-
off_t dec_end = XLogSegmentOffset(Min(data_end.lsn, curr_key->end.lsn), segSize);
418+
419+
/*
420+
* We want to calculate where to start / end encrypting. This
421+
* depends on two factors:
422+
*
423+
* 1. Where does the key start / end
424+
*
425+
* 2. Where does the data start / end
426+
*
427+
* And this is complicated even more by the fact that keys can
428+
* span multiple timelines: if a key starts at TLI 3 LSN 100,
429+
* and ends at TLI 5 LSN 200 it means it is used for
430+
* everything between two, including the entire TLI 4. For
431+
* example, TLI 4 LSN 1 and TLI 4 LSN 400 are both encrypted
432+
* with it, even through 1 is less than 100 and 400 is greater
433+
* than 200.
434+
*
435+
* The below min/max calculations make sure that if the key
436+
* and data are in the same timeline, we only encrypt/decrypt
437+
* in the range of the current key - if the data is longer in
438+
* some directions, we use multiple keys. But if the data
439+
* starts/ends in a TLI "within" the key, we can safely
440+
* decrypt/encrypt from the beginning / until the end, as it
441+
* is part of the key.
442+
*/
443+
444+
445+
size_t end_lsn =
446+
data_end.tli < curr_key->end.tli ? data_end.lsn :
447+
Min(data_end.lsn, curr_key->end.lsn);
448+
size_t start_lsn =
449+
data_start.tli > curr_key->start.tli ? data_start.lsn :
450+
Max(data_start.lsn, curr_key->start.lsn);
451+
off_t dec_off =
452+
XLogSegmentOffset(start_lsn, segSize);
453+
off_t dec_end =
454+
XLogSegmentOffset(end_lsn, segSize);
420455
size_t dec_sz;
421456
char *dec_buf = (char *) buf + (dec_off - offset);
422457

423458
Assert(dec_off >= offset);
424459

425-
CalcXLogPageIVPrefix(tli, segno, curr_key->key.base_iv, iv_prefix);
460+
CalcXLogPageIVPrefix(tli, segno, curr_key->key.base_iv,
461+
iv_prefix);
426462

427-
/* We have reached the end of the segment */
463+
/*
464+
* We have reached the end of the segment
465+
*/
428466
if (dec_end == 0)
429467
{
430468
dec_end = offset + count;

0 commit comments

Comments
 (0)