Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .github/workflows/fuzz.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Seven jobs on a nightly toolchain — only worth running when a change can
# Eight jobs on a nightly toolchain — only worth running when a change can
# reach a decoder. The weekly cron runs unconditionally, so a too-narrow path
# filter costs coverage for a week, not forever.

Expand All @@ -7,10 +7,11 @@ name: ASan fuzz + 32-bit corpus/property
on:
pull_request:
branches: [main]
# The crates the six targets reach.
# The crates the seven targets reach.
paths:
- fuzz/**
- nodedb-codec/**
- nodedb-raster/**
- nodedb-columnar/**
- nodedb-strict/**
- nodedb-wal/**
Expand Down Expand Up @@ -49,6 +50,8 @@ jobs:
dictionary: fuzz/dictionaries/binary.dict
- name: sql_preprocess
dictionary: fuzz/dictionaries/sql.dict
- name: geotiff_decoder
dictionary: fuzz/dictionaries/binary.dict
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# A pinned nightly keeps the build cache key stable across days. Move
Expand All @@ -63,7 +66,7 @@ jobs:
with:
workspaces: fuzz -> target
key: ${{ matrix.target.name }}
# All six matrix jobs install the same pinned binary; build it once.
# All seven matrix jobs install the same pinned binary; build it once.
- name: Cache cargo-fuzz
id: cache-cargo-fuzz
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
Expand Down
38 changes: 37 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ members = [
"nodedb-strict",
"nodedb-columnar",
"nodedb-array",
"nodedb-raster",
"nodedb-sql",
"nodedb-physical",
"nodedb-test-support",
Expand Down Expand Up @@ -58,6 +59,7 @@ nodedb-fts = { path = "nodedb-fts", version = "0.7.0-dev" }
nodedb-strict = { path = "nodedb-strict", version = "0.7.0-dev" }
nodedb-columnar = { path = "nodedb-columnar", version = "0.7.0-dev" }
nodedb-array = { path = "nodedb-array", version = "0.7.0-dev" }
nodedb-raster = { path = "nodedb-raster", version = "0.7.0-dev" }
nodedb-sql = { path = "nodedb-sql", version = "0.7.0-dev" }
nodedb-physical = { path = "nodedb-physical", version = "0.7.0-dev" }
nodedb-test-support = { path = "nodedb-test-support" }
Expand Down Expand Up @@ -94,6 +96,10 @@ libc = "0.2"
# Checksums
crc32c = "0.6"

# GeoTIFF decoding with the deflate and LZW codecs only.
# The raster reader refuses JPEG, fax and WebP compression.
tiff = { version = "0.11.3", default-features = false, features = ["deflate", "lzw"] }

# Crypto
# Pinned to the digest 0.10 generation: `rsa` (JWT RS256) has no release on
# digest 0.11, so sha2 0.11 / hmac 0.13 / aes-gcm 0.11 / p256 0.14 cannot be
Expand Down
4 changes: 3 additions & 1 deletion docs/query-language.md
Original file line number Diff line number Diff line change
Expand Up @@ -1187,12 +1187,14 @@ COPY users TO '/exports/users.ndjson';
COPY (SELECT id, name FROM users WHERE status = 'active') TO '/exports/active.csv' WITH (FORMAT csv);
```

- A server-path `COPY FROM` or `COPY TO` reads or writes as the server's OS user, so it needs superuser. Any other user is `42501`.
- A path must be absolute and must not contain `..`. A relative path is `42601`. A path with `..` is `42501`.
- `COPY FROM` reads regular files only. A FIFO, a device, or a directory is `58030`, also through a symlink.
- `FORMAT` is `ndjson` (or `jsonl`), `json`, or `csv`. Without `FORMAT`, the extension decides: `.ndjson`, `.jsonl`, `.json`, `.csv`. `DELIMITER` is one character. `HEADER` defaults to `true`.
- `COPY <collection> FROM STDIN` is refused with `0A000`. Use the server-path form.
- A collection on the `timeseries` or `spatial` engine refuses `COPY FROM` with `0A000`.
- A file over 16 GiB is `54000`.
- `COPY TO` writes `<path>.tmp` and renames it to `<path>`. It is not transactional.
- `COPY TO` writes `<path>.tmp` and renames it to `<path>`. `<path>.tmp` must not exist, else `58030`. It is not transactional.
- `COPY <array> FROM '<path>'` loads CSV only. Use `WITH (FORMAT csv)`.

### Array bulk load
Expand Down
82 changes: 82 additions & 0 deletions fuzz/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ tempfile = "3"
nodedb-codec = { path = "../nodedb-codec" }
nodedb-columnar = { path = "../nodedb-columnar" }
nodedb-mem = { path = "../nodedb-mem" }
nodedb-raster = { path = "../nodedb-raster" }
nodedb-strict = { path = "../nodedb-strict" }
nodedb-types = { path = "../nodedb-types" }
nodedb-wal = { path = "../nodedb-wal" }
Expand Down Expand Up @@ -62,6 +63,14 @@ doc = false
bench = false
required-features = ["fuzzing"]

[[bin]]
name = "geotiff_decoder"
path = "fuzz_targets/geotiff_decoder.rs"
test = false
doc = false
bench = false
required-features = ["fuzzing"]

[[bin]]
name = "sql_preprocess"
path = "fuzz_targets/sql_preprocess.rs"
Expand Down
Binary file added fuzz/corpus/geotiff_decoder/strip-f32.tif
Binary file not shown.
Binary file not shown.
Binary file added fuzz/corpus/geotiff_decoder/truncated.tif
Binary file not shown.
5 changes: 5 additions & 0 deletions fuzz/fuzz_targets/geotiff_decoder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#![no_main]

use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| nodedb_fuzz::targets::geotiff_decoder::run(data));
34 changes: 34 additions & 0 deletions fuzz/src/targets/geotiff_decoder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//! GeoTIFF open and block decode over arbitrary bytes.
//!
//! Every outcome must be `Ok` or a typed `RasterError`. A decoded block
//! must hold exactly one window of samples per band.

use std::io::Cursor;

use nodedb_raster::{GeoTiffReader, RasterLimits};

/// Per-block decode cap. Small enough that a hostile header cannot make
/// one block a large allocation.
const BLOCK_BYTES: usize = 4 * 1024 * 1024;
/// Blocks decoded per input, so a huge declared grid stays fast.
const MAX_BLOCKS: u32 = 64;

pub fn run(data: &[u8]) {
let Ok(mut reader) = GeoTiffReader::open(Cursor::new(data), RasterLimits::new(BLOCK_BYTES))
else {
return;
};
let _ = reader.srid();
let bands = usize::from(reader.band_count());
let sample_type = reader.sample_type();
for index in 0..reader.block_count().min(MAX_BLOCKS) {
let Ok(block) = reader.read_block(index) else {
continue;
};
assert_eq!(block.bands.len(), bands, "band count of block {index}");
for samples in &block.bands {
assert_eq!(samples.len(), block.window.pixels(), "block {index}");
assert_eq!(samples.sample_type(), sample_type, "block {index}");
}
}
}
Loading
Loading