Skip to content

pgwire accepts any database name and binds the default database, while the native protocol rejects an unknown name #235

Description

@mkhairi

Version / build tested against

origin/main @ 247131e72

Deployment mode

Origin — single node (local)

Engine(s) involved

Not engine-specific / unsure

Summary

The two client protocols disagree about what a connection's database name means. pgwire (6432) never validates it: any string authenticates and is served the bootstrapped default database, including a name that does not exist, and writes issued on that connection land in default. The native protocol (6433) resolves the name through the catalog and rejects an unknown one with 3D000 selected database does not exist. So a connection string that works over pgwire fails at the handshake the moment it is pointed at the native port, and — more quietly — a typo'd or never-created database name reads and writes real data over pgwire with nothing to indicate the requested database was ignored.

Steps to reproduce

-- Fresh data directory. One database exists, and it is named `default`.
SHOW DATABASES;
--   name   | status | created_at_lsn | quota_id | collection_count | tenant_count | parent_clone
-- ---------+--------+----------------+----------+------------------+--------------+--------------
--  default | active | 0              | 0        | 0                | 0            |
-- (1 row)

-- Connect over pgwire naming a database that does not exist:
--   psql -h 127.0.0.1 -p 6432 -U nodedb -d totally_bogus_name
-- Accepted. No warning, no error.
SELECT current_schema();
--  current_schema()
-- ------------------
--  public
-- (1 row)

-- Writes on that connection are not isolated - they land in `default`:
CREATE COLLECTION bogus_db_probe;                      -- CREATE COLLECTION
INSERT INTO bogus_db_probe (id, v) VALUES ('x', 1);    -- INSERT 1

-- Reconnect naming the real database:
--   psql -h 127.0.0.1 -p 6432 -U nodedb -d default
SELECT * FROM bogus_db_probe;
--  id | v
-- ----+---
--  x  | 1
-- (1 row)   <- written through a connection that named a nonexistent database

-- The same three names over the native protocol (6433), same daemon:
--   database => "default"             ACCEPTED, SELECT 1 returns 1
--   database => "nodedb"              REJECTED, 3D000 selected database does not exist
--   database => "totally_bogus_name"  REJECTED, 3D000 selected database does not exist
--
-- Omitting the database field entirely on a native connection succeeds - it
-- falls back to the identity default, then DatabaseId::DEFAULT.

Expected behavior

One rule for both protocols. Either pgwire rejects an unknown database name with 3D000 — matching the native protocol, and matching PostgreSQL (FATAL: database "x" does not exist) — or both protocols fall back to the caller's default database when the requested name is absent.

Rejecting is the safer of the two: silently serving a database other than the one named is how a typo reaches production data.

Actual behavior

pgwire accepts every database name, validates nothing, and binds default; DDL and DML on that connection mutate default. The native protocol rejects any name absent from the catalog with 3D000. Identical credentials therefore succeed on 6432 and fail on 6433.

Severity facts

  • A workaround exists (rewrite the query, avoid one path, etc.)

Not checked: data lost/corrupted/silently wrong; crash/hang/won't start; security or isolation boundary crossed; core functionality broken with no acceptable workaround.

Proposed severity

SEV-2 — High: major functionality broken or silently-wrong results; stored data intact

Reproducibility

Always — every attempt

Last known-good version / commit (if a regression)

Unknown — not established. Whether pgwire ever validated the name, or whether the native check is newer than the pgwire path, was not tested against an earlier build.

Environment & logs

Linux x86_64, release build, single node, fresh data directory. Ports: pgwire 6432, native 6433, auth.mode = "password".

No server log output on the pgwire path — the name is never looked up, so there is nothing to log.

Files (best-guess): nodedb/src/control/server/native/session/auth.rs holds the native check — it resolves RequestFields::Text(f).database via catalog.get_database_id_by_name, returns 3D000 on Ok(None), then re-validates the descriptor with catalog.get_database(db_id) so the default/fallback selection is checked too. The pgwire startup path has no equivalent lookup. bootstrap_default_database in nodedb/src/control/security/catalog/database.rs is what names the bootstrapped database default (DatabaseId(0)).

Before submitting

  • I searched existing issues and this is not a duplicate.
  • I reproduced this on a released tag or a current main build (not a stale local branch).
  • This is not a security vulnerability (those go to a private advisory).

Metadata

Metadata

Assignees

No one assigned

    Labels

    status:needs-triageAwaiting maintainer triage (severity + priority)type:bugA defect — broken, incorrect, or lost data

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions