agora inbox for pgsql-bugs@postgresql.org  
help / color / mirror / Atom feed
From: PG Bug reporting form <noreply@postgresql.org>
To: pgsql-bugs@lists.postgresql.org
Cc: theshallow27@gmail.com
Subject: BUG #19730: PostgreSQL `pg_backup_start` accepts newlines in the backup label, producing an unrestorable `backup
Date: Tue, 29 Sep 2026 19:15:42 +0000
Message-ID: <19730-85a6044c9e72774a@postgresql.org> (raw)

The following bug has been logged on the website:

Bug reference:      19730
Logged by:          Shallow
Email address:      theshallow27@gmail.com
PostgreSQL version: 18.6
Operating system:   Linux
Description:        

Summary

`pg_backup_start()` accepts a label containing a newline and embeds it
verbatim,
unescaped, into the line-oriented `backup_label` contents returned by
`pg_backup_stop()`. The extra line(s) shift the fields that follow `LABEL:`,
so
the file the documentation requires to be written "byte for byte without
modification" is malformed, and the server's own `read_backup_label()`
rejects
it with `FATAL` at restore time. The same function already backslash-escapes
`\n`/`\r` when building `tablespace_map`, but does nothing for the label.


Reproducing the Bug

```python
import db_harness

db_harness.query("""
CREATE OR REPLACE FUNCTION repro(lbl text) RETURNS text LANGUAGE plpgsql AS
$$
DECLARE r record;
BEGIN
  PERFORM pg_backup_start(lbl, true);
  SELECT * INTO r FROM pg_backup_stop(false);
  RETURN r.labelfile;
END $$;
""")

label = "mybackup\nINCREMENTAL FROM LSN: 0/0"
r = db_harness.query("SELECT repro(%s)", [label])
assert r.ok, r.error
print(r.rows[0][0])
```

Output — the `labelfile` that the documentation says must be written
verbatim to
`<backup>/backup_label`:

```
START WAL LOCATION: 5/BC000028 (file 0000000100000005000000BC)
CHECKPOINT LOCATION: 5/BC000080
BACKUP METHOD: streamed
BACKUP FROM: primary
START TIME: 2026-09-26 08:46:09 UTC
LABEL: mybackup
INCREMENTAL FROM LSN: 0/0
START TIMELINE: 1
```

Restoring from that backup makes the server refuse to start:

```
FATAL:  this is an incremental backup, not a data directory
HINT:  Use pg_combinebackup to reconstruct a valid data directory.
```

With `label = "mybackup\nSTART TIMELINE: 0"` the same procedure yields:

```
FATAL:  invalid data in file "backup_label"
DETAIL:  Timeline ID parsed is 0, but expected 1.
```

Fix

Reject labels containing a newline or carriage return, alongside the
existing
length check. Escaping is not an option for the label without also changing
`read_backup_label()`, whose `%1023[^\n]` conversion does no de-escaping;
rejecting at `pg_backup_start()` time fails loudly and early, before a
useless
backup is taken.

```diff
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -8861,6 +8861,17 @@ do_pg_backup_start(const char *backupidstr, bool
fast, List **tablespaces,
        if (strlen(backupidstr) > MAXPGPATH)
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 errmsg("backup label too long (max %d
bytes)",
                                                MAXPGPATH)));
 
+       /*
+        * The label is written as a single line of the backup_label file,
and read
+        * back with a conversion that stops at a newline and does no
de-escaping.
+        * An embedded newline would shift every field after "LABEL:" and
make the
+        * resulting backup_label unreadable at recovery time, so refuse it
here
+        * rather than producing an unrestorable backup.
+        */
+       if (strpbrk(backupidstr, "\n\r") != NULL)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("backup label must not contain
newline or carriage return characters")));
+
        strlcpy(state->name, backupidstr, sizeof(state->name));
```








view thread (7+ messages)  latest in thread

Message-ID: <19730-85a6044c9e72774a@postgresql.org>
Permalink:  ../19730-85a6044c9e72774a@postgresql.org/
Also on:    postgresql.org/message-id/19730-85a6044c9e72774a@postgresql.org

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: pgsql-bugs@postgresql.org
  Cc: noreply@postgresql.org, pgsql-bugs@lists.postgresql.org, theshallow27@gmail.com
  Subject: Re: BUG #19730: PostgreSQL `pg_backup_start` accepts newlines in the backup label, producing an unrestorable `backup
  In-Reply-To: <19730-85a6044c9e72774a@postgresql.org>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox