From 050b1a9451dcfa545b7efbccf500028c9b0192e2 Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Wed, 13 May 2026 14:30:24 -0600 Subject: [PATCH 1/3] lib: cfl: ugprade to v0.7.1 Signed-off-by: Eduardo Silva --- lib/cfl/.github/workflows/build.yaml | 55 +-- lib/cfl/.github/workflows/lint.yaml | 4 +- lib/cfl/.github/workflows/packages.yaml | 10 +- lib/cfl/AGENTS.md | 76 +++ lib/cfl/CMakeLists.txt | 2 +- lib/cfl/README.md | 71 ++- lib/cfl/include/cfl/cfl.h | 2 + lib/cfl/include/cfl/cfl_array.h | 22 + lib/cfl/include/cfl/cfl_atomic.h | 31 ++ lib/cfl/include/cfl/cfl_checksum.h | 1 + lib/cfl/include/cfl/cfl_container.h | 59 +++ lib/cfl/include/cfl/cfl_kv.h | 2 + lib/cfl/include/cfl/cfl_kvlist.h | 18 +- lib/cfl/include/cfl/cfl_list.h | 52 ++ lib/cfl/include/cfl/cfl_object.h | 5 + lib/cfl/include/cfl/cfl_sds.h | 15 +- lib/cfl/include/cfl/cfl_time.h | 2 +- lib/cfl/include/cfl/cfl_utils.h | 2 + lib/cfl/include/cfl/cfl_variant.h | 5 + lib/cfl/src/CMakeLists.txt | 77 ++- lib/cfl/src/cfl.c | 3 +- lib/cfl/src/cfl_array.c | 110 ++++- lib/cfl/src/cfl_atomic_clang.c | 42 ++ lib/cfl/src/cfl_atomic_gcc.c | 42 ++ lib/cfl/src/cfl_atomic_generic.c | 121 +++++ lib/cfl/src/cfl_atomic_msvc.c | 160 +++++++ lib/cfl/src/cfl_checksum.c | 4 + lib/cfl/src/cfl_container.c | 601 ++++++++++++++++++++++++ lib/cfl/src/cfl_kv.c | 39 +- lib/cfl/src/cfl_kvlist.c | 286 ++++++++++- lib/cfl/src/cfl_object.c | 86 +++- lib/cfl/src/cfl_sds.c | 176 +++++-- lib/cfl/src/cfl_utils.c | 76 ++- lib/cfl/src/cfl_variant.c | 169 ++++++- lib/cfl/tests/CMakeLists.txt | 53 +++ lib/cfl/tests/array.c | 205 ++++++++ lib/cfl/tests/atomic_operations.c | 177 +++++++ lib/cfl/tests/checksum.c | 38 ++ lib/cfl/tests/headers.c | 49 ++ lib/cfl/tests/kv.c | 23 + lib/cfl/tests/kvlist.c | 356 ++++++++++++++ lib/cfl/tests/object.c | 286 +++++++++++ lib/cfl/tests/sds.c | 123 +++++ lib/cfl/tests/utils.c | 15 + lib/cfl/tests/variant.c | 166 ++++++- 45 files changed, 3752 insertions(+), 165 deletions(-) create mode 100644 lib/cfl/AGENTS.md create mode 100644 lib/cfl/include/cfl/cfl_atomic.h create mode 100644 lib/cfl/include/cfl/cfl_container.h create mode 100644 lib/cfl/src/cfl_atomic_clang.c create mode 100644 lib/cfl/src/cfl_atomic_gcc.c create mode 100644 lib/cfl/src/cfl_atomic_generic.c create mode 100644 lib/cfl/src/cfl_atomic_msvc.c create mode 100644 lib/cfl/src/cfl_container.c create mode 100644 lib/cfl/tests/atomic_operations.c create mode 100644 lib/cfl/tests/checksum.c create mode 100644 lib/cfl/tests/headers.c diff --git a/lib/cfl/.github/workflows/build.yaml b/lib/cfl/.github/workflows/build.yaml index b33123dc1cf..22f3d80057d 100644 --- a/lib/cfl/.github/workflows/build.yaml +++ b/lib/cfl/.github/workflows/build.yaml @@ -15,10 +15,10 @@ jobs: strategy: fail-fast: false matrix: - os: [windows-latest, windows-2019] + os: [windows-latest, windows-2022] steps: - - uses: actions/checkout@v4 - - name: Build on ${{ matrix.os }} with vs-2019 + - uses: actions/checkout@v6 + - name: Build on ${{ matrix.os }} with MSVC run: | .\scripts\win_build.bat - name: Run unit tests. @@ -31,9 +31,9 @@ jobs: strategy: fail-fast: false matrix: - os: [windows-latest, windows-2019] + os: [windows-latest, windows-2022] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Get dependencies w/ chocolatey uses: crazy-max/ghaction-chocolatey@v3 with: @@ -106,7 +106,11 @@ jobs: steps: - name: Set up base image dependencies run: | - apt-get update + printf '%s\n' \ + 'deb http://archive.debian.org/debian buster main' \ + 'deb http://archive.debian.org/debian-security buster/updates main' \ + > /etc/apt/sources.list + apt-get -o Acquire::Check-Valid-Until=false update apt-get install -y build-essential wget make gcc g++ - name: Install CMake 3.20.0 @@ -122,7 +126,7 @@ jobs: # Confirm CMake installation /usr/local/bin/cmake --version - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Run compilation run: | @@ -138,7 +142,7 @@ jobs: os: [ubuntu-latest] compiler: [ gcc, clang ] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Build on ${{ matrix.os }} with ${{ matrix.compiler }} uses: uraimo/run-on-arch-action@v3.0.1 with: @@ -167,7 +171,7 @@ jobs: os: [ubuntu-latest, macos-latest] compiler: [ gcc, clang ] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Build on ${{ matrix.os }} with ${{ matrix.compiler }} run: | echo "CC = $CC, CXX = $CXX" @@ -193,36 +197,21 @@ jobs: contents: read runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: submodules: true - dependencies_debian: 'wget' - - name: Install CMake 3.20.0 - run: | - CMAKE_VERSION=3.20.0 - wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-x86_64.sh - chmod +x cmake-${CMAKE_VERSION}-linux-x86_64.sh - - # Create a writable temporary directory - mkdir -p /tmp/cmake - - # Install CMake to /tmp/cmake - ./cmake-${CMAKE_VERSION}-linux-x86_64.sh --skip-license --prefix=/tmp/cmake - - # Add CMake to the PATH - echo 'export PATH=/tmp/cmake/bin:$PATH' >> ~/.bashrc - source ~/.bashrc - - # Verify installation - /tmp/cmake/bin/cmake --version - - - uses: actions/checkout@v4 + - uses: docker://lpenz/ghaction-cmake:0.19 with: + pre_command: | + CMAKE_VERSION=3.20.0 + curl -fsSLO https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-x86_64.sh + chmod +x cmake-${CMAKE_VERSION}-linux-x86_64.sh + ./cmake-${CMAKE_VERSION}-linux-x86_64.sh --skip-license --prefix=/usr/local + cmake --version preset: ${{ matrix.preset }} - cmakeflags: '-DCFL_TESTS=On -DCFL_DEV=on .' - build_command: /tmp/cmake/bin/cmake && make all + build_command: make all # this job provides the single required status for PRs to be merged into main. # instead of updating the protected branch status in github, developers can update the needs section below diff --git a/lib/cfl/.github/workflows/lint.yaml b/lib/cfl/.github/workflows/lint.yaml index e18e66b44f2..12f97917cf4 100644 --- a/lib/cfl/.github/workflows/lint.yaml +++ b/lib/cfl/.github/workflows/lint.yaml @@ -10,7 +10,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: ludeeus/action-shellcheck@master with: ignore_paths: lib @@ -21,7 +21,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - run: | echo "::add-matcher::.github/actionlint-matcher.json" bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) diff --git a/lib/cfl/.github/workflows/packages.yaml b/lib/cfl/.github/workflows/packages.yaml index 1611b7a676b..a73e49b19ae 100644 --- a/lib/cfl/.github/workflows/packages.yaml +++ b/lib/cfl/.github/workflows/packages.yaml @@ -18,7 +18,7 @@ jobs: matrix: format: [ rpm, deb ] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: uraimo/run-on-arch-action@v3.0.1 name: Build the ${{matrix.format}} packages with: @@ -36,7 +36,7 @@ jobs: echo ${{ matrix.format }} | awk '{print toupper($0)}' | xargs -I{} cpack -G {} - name: Store the master package artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: ${{ matrix.format }}-arm64 path: | @@ -51,14 +51,14 @@ jobs: runs-on: [ ubuntu-latest ] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Build the ${{matrix.format}} packages run: | cmake . echo ${{ matrix.format }} | awk '{print toupper($0)}' | xargs -I{} cpack -G {} - name: Store the master package artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: ${{ matrix.format }}-amd64 path: | @@ -74,7 +74,7 @@ jobs: contents: write steps: - name: Download all artefacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: path: artifacts/ diff --git a/lib/cfl/AGENTS.md b/lib/cfl/AGENTS.md new file mode 100644 index 00000000000..8fccc629736 --- /dev/null +++ b/lib/cfl/AGENTS.md @@ -0,0 +1,76 @@ +# Repository Guidelines + +## Preferred Commands +- Configure with tests: `cmake -S . -B build -DCFL_TESTS=On` +- Build: `cmake --build build -j8` +- Run all tests: `ctest --test-dir build --output-on-failure` +- Run a focused test: `ctest --test-dir build -R --output-on-failure` +- Check staged or local patches for whitespace before closing a change: + `git diff --check` + +## Project Structure & Module Organization +CFL is a small C library built with CMake. + +- `include/cfl/`: public CFL headers. +- `src/`: library implementation files. +- `tests/`: acutest-based unit tests. +- `lib/xxhash/`: bundled xxHash dependency. +- `cmake/`: project CMake helpers. + +Keep changes scoped to the affected module. Put public declarations in +`include/cfl/`, implementation in `src/`, and matching unit coverage in +`tests/` when behavior changes. + +## Build, Test, and Development Commands +- `cmake -S . -B build -DCFL_TESTS=On`: configure the project with tests. +- `cmake --build build -j8`: compile the static library and tests. +- `ctest --test-dir build --output-on-failure`: run the enabled test suite. +- `ctest --test-dir build -R cfl-test- --output-on-failure`: run a + focused unit test. + +Prefer targeted test runs while iterating, then run the full enabled suite +before closing changes that touch shared code or public APIs. + +## Coding Style & Naming Conventions +- Follow the existing Apache-style C conventions used in this repository. +- Use 4-space indentation and keep lines readable; avoid unnecessary wrapping. +- Always use braces for `if/else/while/do` blocks. +- Put function opening braces on the next line: + `int fn(void)\n{ ... }` +- Declare variables at the start of functions, not mid-block. +- Prefer descriptive `snake_case` names with the `cfl_` prefix for public APIs. +- Use `CFL_TRUE` and `CFL_FALSE` for CFL boolean-style return values. +- Use `/* ... */` comments, and add comments only where they clarify non-obvious + behavior. +- Keep public headers self-contained by including the standard headers they need. + +## Testing Guidelines +- Add or update acutest unit coverage for behavior changes. +- Keep tests close to the affected module and name test binaries through + `tests/CMakeLists.txt`. +- Validate both success and failure paths for parsers, containers, allocation + handling, and boundary conditions. +- Run broader coverage when changing shared headers, CMake wiring, memory + ownership, or common data structures. +- If a relevant test cannot be run, report the exact blocker in the final + response. + +## Commit & Pull Request Guidelines +- Follow observed local history style: + `component: short imperative description` + Examples: `sds: do not export internal sds_alloc function`, + `build: bump to v0.6.2`, `atomic: add atomic operations API`. +- Keep each commit scoped to one component or interface. +- Keep subject/body lines concise; use a body when the reason or scope is not + obvious from the subject. +- Do not mix unrelated code and documentation updates in one commit unless the + user explicitly asks for a combined commit. +- Do not rewrite history, amend commits, create remote branches, or open pull + requests unless explicitly requested. + +## Agent Action Limits +- Do not modify repositories or files outside this project unless the user + explicitly asks. +- Do not revert user changes outside the requested scope. +- Preserve unrelated untracked or modified files in the worktree. +- Prefer minimal patches that avoid unrelated formatting or refactoring churn. diff --git a/lib/cfl/CMakeLists.txt b/lib/cfl/CMakeLists.txt index 4895700264a..93a189936ec 100644 --- a/lib/cfl/CMakeLists.txt +++ b/lib/cfl/CMakeLists.txt @@ -5,7 +5,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # C Floppy Version set(CFL_VERSION_MAJOR 0) -set(CFL_VERSION_MINOR 6) +set(CFL_VERSION_MINOR 7) set(CFL_VERSION_PATCH 1) set(CFL_VERSION_STR "${CFL_VERSION_MAJOR}.${CFL_VERSION_MINOR}.${CFL_VERSION_PATCH}") diff --git a/lib/cfl/README.md b/lib/cfl/README.md index 4050bef8b27..c81b4bafd22 100644 --- a/lib/cfl/README.md +++ b/lib/cfl/README.md @@ -1,18 +1,71 @@ # CFL -CFL is a tiny library that provides interfaces for data structures, originally created to satisfy the needs of Fluent Bit and other libraries used internally like CMetrics and CTraces projects. +CFL is a tiny C library that provides small data-structure and utility +interfaces. It was originally created to satisfy the needs of Fluent Bit and +related libraries such as CMetrics and CTraces. -note: The name doesn't mean anything specific, you can call it `c:\ floppy` if you want. +Note: The name does not mean anything specific; you can call it `c:\ floppy` +if you want. ## Interfaces -- cfl_sds: string manipulation -- cfl_list: linked list -- cfl_kv: key value pairs by using a linked list (cfl_list) -- cfl_array: array of elements -- cfl_variant: interface to manage contexts with vairant types -- cfl_time: time utilities -- cfl_hash: 64bit hashing functions +Applications can include `` to pull in the common CFL interfaces. +Specialized headers are also available under `include/cfl/` when a caller only +needs one module. + +### Core + +- `cfl_init()`: initializes library-level facilities. +- `cfl_version()`: returns the CFL version string. +- `CFL_TRUE` and `CFL_FALSE`: common boolean-style constants used by CFL APIs. + +### Data Structures + +- `cfl_sds`: dynamic string storage with explicit length and allocation + tracking. It supports creation from strings or buffers, growth, concatenation, + formatted writes, length updates, and destruction. +- `cfl_list`: intrusive doubly linked list helpers. It provides initialization, + add, append, prepend, delete, concatenate, size, entry lookup, and safe + iteration macros. +- `cfl_kv`: SDS-backed string key/value entries stored in a `cfl_list`. This is + useful for simple string maps where values are plain strings. +- `cfl_variant`: tagged value container for bool, signed integer, unsigned + integer, double, null, reference, string, bytes, array, and key/value list + values. +- `cfl_array`: ordered collection of `cfl_variant` entries. It supports fixed + or resizable arrays, append helpers for all variant types, fetch by index, + removal, size inspection, and printing. +- `cfl_kvlist`: string-keyed map whose values are `cfl_variant` instances. It + supports typed insert helpers, size-aware key APIs, fetch, contains, remove, + count, and printing. +- `cfl_object`: generic wrapper that can hold a `cfl_kvlist`, `cfl_variant`, or + `cfl_array` and print the associated value. + +### Utilities + +- `cfl_atomic`: 64-bit atomic initialization, compare-exchange, store, and load + operations with platform-specific backends. +- `cfl_time`: wall-clock timestamp helper that returns nanoseconds. +- `cfl_hash`: naming wrappers around xxHash 64-bit and 128-bit hashing APIs. +- `cfl_checksum`: CRC32C checksum helper. +- `cfl_utils`: string split helpers, including quote-aware splitting, with + results returned as `cfl_list` entries. +- `cfl_log`: runtime error reporting helpers. + +### Support Headers + +- `cfl_compat`: platform compatibility macros. +- `cfl_found`: lightweight include-probe helper for parent projects. +- `cfl_info`: generated build information and feature flags. +- `cfl_version`: version macros. + +## Build and Test + +```sh +cmake -S . -B build -DCFL_TESTS=On +cmake --build build -j8 +ctest --test-dir build --output-on-failure +``` ## License diff --git a/lib/cfl/include/cfl/cfl.h b/lib/cfl/include/cfl/cfl.h index 921767e8091..3491e0c8bcb 100644 --- a/lib/cfl/include/cfl/cfl.h +++ b/lib/cfl/include/cfl/cfl.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ #include #include #include +#include #include #include #include diff --git a/lib/cfl/include/cfl/cfl_array.h b/lib/cfl/include/cfl/cfl_array.h index 331a4cbc6b6..f057bf4539a 100644 --- a/lib/cfl/include/cfl/cfl_array.h +++ b/lib/cfl/include/cfl/cfl_array.h @@ -21,13 +21,21 @@ #define CFL_ARRAY_H #include +#include +#include + #include +struct cfl_kvlist; + struct cfl_array { int resizable; struct cfl_variant **entries; size_t slot_count; size_t entry_count; + struct cfl_variant *owner; + struct cfl_array *parent_array; + struct cfl_kvlist *parent_kvlist; }; struct cfl_array *cfl_array_create(size_t slot_count); @@ -36,6 +44,10 @@ void cfl_array_destroy(struct cfl_array *array); static inline struct cfl_variant *cfl_array_fetch_by_index(struct cfl_array *array, size_t position) { + if (array == NULL) { + return NULL; + } + if (position >= array->entry_count) { return NULL; } @@ -45,9 +57,19 @@ static inline struct cfl_variant *cfl_array_fetch_by_index(struct cfl_array *arr static inline size_t cfl_array_size(struct cfl_array *array) { + if (array == NULL) { + return 0; + } + return array->entry_count; } +/* + * Append APIs take ownership of the value on success. A raw array or kvlist + * must have one owning variant at a time. To move an existing kvpair value, + * detach it with cfl_kvpair_take_value() before reinserting it. Do not leave + * the same variant pointer attached to multiple live containers. + */ int cfl_array_append(struct cfl_array *array, struct cfl_variant *value); int cfl_array_append_string(struct cfl_array *array, char *value); int cfl_array_append_string_s(struct cfl_array *array, char *str, size_t str_len, int referenced); diff --git a/lib/cfl/include/cfl/cfl_atomic.h b/lib/cfl/include/cfl/cfl_atomic.h new file mode 100644 index 00000000000..e113a991afb --- /dev/null +++ b/lib/cfl/include/cfl/cfl_atomic.h @@ -0,0 +1,31 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CFL + * === + * Copyright (C) 2022 The CFL Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CFL_ATOMIC_H +#define CFL_ATOMIC_H + +#include + +int cfl_atomic_initialize(); +int cfl_atomic_compare_exchange(uint64_t *storage, uint64_t old_value, + uint64_t new_value); +void cfl_atomic_store(uint64_t *storage, uint64_t new_value); +uint64_t cfl_atomic_load(uint64_t *storage); + +#endif diff --git a/lib/cfl/include/cfl/cfl_checksum.h b/lib/cfl/include/cfl/cfl_checksum.h index d9690aa8ca2..32e26fbf63f 100644 --- a/lib/cfl/include/cfl/cfl_checksum.h +++ b/lib/cfl/include/cfl/cfl_checksum.h @@ -20,6 +20,7 @@ #ifndef CFL_CHECKSUM_H #define CFL_CHECKSUM_H +#include #include uint32_t cfl_checksum_crc32c(unsigned char *buffer, size_t length); diff --git a/lib/cfl/include/cfl/cfl_container.h b/lib/cfl/include/cfl/cfl_container.h new file mode 100644 index 00000000000..714df85d1da --- /dev/null +++ b/lib/cfl/include/cfl/cfl_container.h @@ -0,0 +1,59 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CFL + * === + * Copyright (C) 2026 The CFL Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CFL_CONTAINER_H +#define CFL_CONTAINER_H + +#include +#include +#include + +int cfl_container_array_contains_array(struct cfl_array *array, + struct cfl_array *target); +int cfl_container_array_contains_kvlist(struct cfl_array *array, + struct cfl_kvlist *target); +int cfl_container_array_contains_variant(struct cfl_array *array, + struct cfl_variant *target); + +int cfl_container_kvlist_contains_array(struct cfl_kvlist *kvlist, + struct cfl_array *target); +int cfl_container_kvlist_contains_kvlist(struct cfl_kvlist *kvlist, + struct cfl_kvlist *target); +int cfl_container_kvlist_contains_variant(struct cfl_kvlist *kvlist, + struct cfl_variant *target); + +int cfl_container_variant_contains_array(struct cfl_variant *variant, + struct cfl_array *target); +int cfl_container_variant_contains_kvlist(struct cfl_variant *variant, + struct cfl_kvlist *target); +int cfl_container_variant_contains_variant(struct cfl_variant *variant, + struct cfl_variant *target); + +int cfl_container_claim_array(struct cfl_array *array, + struct cfl_variant *owner); +int cfl_container_claim_kvlist(struct cfl_kvlist *kvlist, + struct cfl_variant *owner); +int cfl_container_adopt_variant(struct cfl_variant *variant); +int cfl_container_move_variant_to_array(struct cfl_array *array, + struct cfl_variant *variant); +int cfl_container_move_variant_to_kvlist(struct cfl_kvlist *kvlist, + struct cfl_variant *variant); +void cfl_container_release_variant(struct cfl_variant *variant); + +#endif diff --git a/lib/cfl/include/cfl/cfl_kv.h b/lib/cfl/include/cfl/cfl_kv.h index f44c5be63d9..812820ecc39 100644 --- a/lib/cfl/include/cfl/cfl_kv.h +++ b/lib/cfl/include/cfl/cfl_kv.h @@ -20,6 +20,8 @@ #ifndef CFL_KV_H #define CFL_KV_H +#include + #include #include #include diff --git a/lib/cfl/include/cfl/cfl_kvlist.h b/lib/cfl/include/cfl/cfl_kvlist.h index ab25b162389..cd8d90487a9 100644 --- a/lib/cfl/include/cfl/cfl_kvlist.h +++ b/lib/cfl/include/cfl/cfl_kvlist.h @@ -21,10 +21,15 @@ #define CFL_KVLIST_H #include +#include +#include + #include #include #include +struct cfl_array; + struct cfl_kvpair { cfl_sds_t key; /* Key */ struct cfl_variant *val; /* Value */ @@ -32,12 +37,22 @@ struct cfl_kvpair { }; struct cfl_kvlist { - struct cfl_list list; + struct cfl_list list; + struct cfl_variant *owner; + struct cfl_array *parent_array; + struct cfl_kvlist *parent_kvlist; }; struct cfl_kvlist *cfl_kvlist_create(); void cfl_kvlist_destroy(struct cfl_kvlist *list); +/* + * Insert APIs take ownership of array, kvlist, and variant values on success. + * A raw array or kvlist must have one owning variant at a time. To move an + * existing kvpair value, detach it with cfl_kvpair_take_value() before + * reinserting it. Do not leave the same variant pointer attached to multiple + * live containers. + */ int cfl_kvlist_insert_string(struct cfl_kvlist *list, char *key, char *value); @@ -128,6 +143,7 @@ struct cfl_variant *cfl_kvlist_fetch_s(struct cfl_kvlist *list, char *key, size_ int cfl_kvlist_contains(struct cfl_kvlist *kvlist, char *name); int cfl_kvlist_remove(struct cfl_kvlist *kvlist, char *name); void cfl_kvpair_destroy(struct cfl_kvpair *pair); +struct cfl_variant *cfl_kvpair_take_value(struct cfl_kvpair *pair); #endif diff --git a/lib/cfl/include/cfl/cfl_list.h b/lib/cfl/include/cfl/cfl_list.h index f2709288017..47d6ea1cc64 100644 --- a/lib/cfl/include/cfl/cfl_list.h +++ b/lib/cfl/include/cfl/cfl_list.h @@ -31,6 +31,14 @@ #include #include +#ifndef CFL_FALSE +#define CFL_FALSE 0 +#endif + +#ifndef CFL_TRUE +#define CFL_TRUE !CFL_FALSE +#endif + #ifdef _WIN32 /* Windows */ #define cfl_container_of(address, type, field) ((type *)( \ @@ -53,6 +61,10 @@ struct cfl_list { static inline int cfl_list_is_empty(struct cfl_list *head) { + if (head == NULL) { + return 1; + } + if (head->next == head) { return 1; } @@ -62,6 +74,10 @@ static inline int cfl_list_is_empty(struct cfl_list *head) static inline void cfl_list_init(struct cfl_list *list) { + if (list == NULL) { + return; + } + list->next = list; list->prev = list; } @@ -69,12 +85,20 @@ static inline void cfl_list_init(struct cfl_list *list) static inline void __cfl_list_del(struct cfl_list *prev, struct cfl_list *next) { + if (prev == NULL || next == NULL) { + return; + } + prev->next = next; next->prev = prev; } static inline void cfl_list_del(struct cfl_list *entry) { + if (entry == NULL) { + return; + } + __cfl_list_del(entry->prev, entry->next); entry->prev = NULL; @@ -85,6 +109,10 @@ static inline void __cfl_list_add(struct cfl_list *_new, struct cfl_list *prev, struct cfl_list *next) { + if (_new == NULL || prev == NULL || next == NULL) { + return; + } + next->prev = _new; _new->next = next; _new->prev = prev; @@ -94,6 +122,10 @@ static inline void __cfl_list_add(struct cfl_list *_new, static inline void cfl_list_add(struct cfl_list *_new, struct cfl_list *head) { + if (_new == NULL || head == NULL) { + return; + } + __cfl_list_add(_new, head->prev, head); } @@ -134,6 +166,10 @@ static inline void cfl_list_add_before(struct cfl_list *_new, static inline void cfl_list_append(struct cfl_list *_new, struct cfl_list *head) { + if (_new == NULL || head == NULL) { + return; + } + if (cfl_list_is_empty(head)) { __cfl_list_add(_new, head->prev, head); } @@ -147,6 +183,10 @@ static inline void cfl_list_append(struct cfl_list *_new, static inline void cfl_list_prepend(struct cfl_list *_new, struct cfl_list *head) { + if (_new == NULL || head == NULL) { + return; + } + if (cfl_list_is_empty(head)) { __cfl_list_add(_new, head->prev, head); } @@ -162,6 +202,10 @@ static inline int cfl_list_size(struct cfl_list *head) int ret = 0; struct cfl_list *it; + if (head == NULL) { + return 0; + } + for (it = head->next; it != head; it = it->next, ret++); return ret; @@ -175,6 +219,10 @@ static inline void cfl_list_entry_init(struct cfl_list *entry) static inline int cfl_list_entry_is_orphan(struct cfl_list *entry) { + if (entry == NULL) { + return CFL_TRUE; + } + if (entry->next != NULL && entry->prev != NULL) { return CFL_FALSE; @@ -187,6 +235,10 @@ static inline void cfl_list_cat(struct cfl_list *list, struct cfl_list *head) { struct cfl_list *last; + if (list == NULL || head == NULL) { + return; + } + last = head->prev; last->next = list->next; list->next->prev = last; diff --git a/lib/cfl/include/cfl/cfl_object.h b/lib/cfl/include/cfl/cfl_object.h index f959ac4ef3b..97503c4cc02 100644 --- a/lib/cfl/include/cfl/cfl_object.h +++ b/lib/cfl/include/cfl/cfl_object.h @@ -20,6 +20,11 @@ #ifndef CFL_OBJECT_H #define CFL_OBJECT_H +#include + +#include +#include + enum { CFL_OBJECT_NONE = 0, CFL_OBJECT_KVLIST = 1, diff --git a/lib/cfl/include/cfl/cfl_sds.h b/lib/cfl/include/cfl/cfl_sds.h index 9ec5b347ed5..a923882a616 100644 --- a/lib/cfl/include/cfl/cfl_sds.h +++ b/lib/cfl/include/cfl/cfl_sds.h @@ -28,6 +28,7 @@ #include #include #include +#include #define CFL_SDS_HEADER_SIZE (sizeof(uint64_t) + sizeof(uint64_t)) @@ -45,7 +46,19 @@ struct cfl_sds { static inline void cfl_sds_len_set(cfl_sds_t s, size_t len) { - CFL_SDS_HEADER(s)->len = len; + struct cfl_sds *head; + + if (s == NULL) { + return; + } + + head = CFL_SDS_HEADER(s); + if (len > head->alloc) { + return; + } + + head->len = len; + s[len] = '\0'; } size_t cfl_sds_avail(cfl_sds_t s); diff --git a/lib/cfl/include/cfl/cfl_time.h b/lib/cfl/include/cfl/cfl_time.h index 9c141c1b880..592978f0814 100644 --- a/lib/cfl/include/cfl/cfl_time.h +++ b/lib/cfl/include/cfl/cfl_time.h @@ -20,7 +20,7 @@ #ifndef CFL_TIME_H #define CFL_TIME_H -#include +#include uint64_t cfl_time_now(); diff --git a/lib/cfl/include/cfl/cfl_utils.h b/lib/cfl/include/cfl/cfl_utils.h index db9d873ee1c..e4b7dc99c03 100644 --- a/lib/cfl/include/cfl/cfl_utils.h +++ b/lib/cfl/include/cfl/cfl_utils.h @@ -2,7 +2,9 @@ #define CFL_UTILS_H #include /* off_t */ + #include +#include #include struct cfl_split_entry { diff --git a/lib/cfl/include/cfl/cfl_variant.h b/lib/cfl/include/cfl/cfl_variant.h index 4f9953a6956..efdcfe96bfa 100644 --- a/lib/cfl/include/cfl/cfl_variant.h +++ b/lib/cfl/include/cfl/cfl_variant.h @@ -23,6 +23,10 @@ #include #include #include +#include +#include + +#include #define CFL_VARIANT_BOOL 1 #define CFL_VARIANT_INT 2 @@ -52,6 +56,7 @@ struct cfl_variant { * a copy of the original data. */ uint8_t referenced; + uint8_t owned; /* the data */ union { diff --git a/lib/cfl/src/CMakeLists.txt b/lib/cfl/src/CMakeLists.txt index f09a5c3d8b1..de94f0007a8 100644 --- a/lib/cfl/src/CMakeLists.txt +++ b/lib/cfl/src/CMakeLists.txt @@ -1,3 +1,5 @@ +include(CheckCSourceCompiles) + set(src cfl.c cfl_log.c @@ -8,13 +10,86 @@ set(src cfl_object.c cfl_array.c cfl_variant.c + cfl_container.c cfl_checksum.c cfl_utils.c ) +set(CFL_ATOMIC_NEEDS_THREADS Off) +set(CFL_ATOMIC_NEEDS_LIBATOMIC Off) +set(CFL_ATOMIC_USES_BUILTINS Off) + +set(CFL_ATOMIC_BUILTINS_LINK_SOURCE " + #include + int main(void) { + uint64_t storage = 0; + uint64_t expected = 0; + uint64_t desired = 1; + __atomic_store_n(&storage, desired, __ATOMIC_SEQ_CST); + (void) __atomic_load_n(&storage, __ATOMIC_SEQ_CST); + (void) __atomic_compare_exchange(&storage, &expected, &desired, 0, + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return 0; + }") + +if("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") + set(PLATFORM_SPECIFIC_ATOMIC_MODULE cfl_atomic_msvc.c) +elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") + set(PLATFORM_SPECIFIC_ATOMIC_MODULE cfl_atomic_clang.c) + set(CFL_ATOMIC_USES_BUILTINS On) +elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang") + set(PLATFORM_SPECIFIC_ATOMIC_MODULE cfl_atomic_clang.c) + set(CFL_ATOMIC_USES_BUILTINS On) +elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") + set(PLATFORM_SPECIFIC_ATOMIC_MODULE cfl_atomic_gcc.c) + set(CFL_ATOMIC_USES_BUILTINS On) +else() + set(PLATFORM_SPECIFIC_ATOMIC_MODULE cfl_atomic_generic.c) + set(CFL_ATOMIC_NEEDS_THREADS On) +endif() + +if(CFL_ATOMIC_USES_BUILTINS) + check_c_source_compiles("${CFL_ATOMIC_BUILTINS_LINK_SOURCE}" + CFL_ATOMIC_BUILTINS_LINK) + + if(NOT CFL_ATOMIC_BUILTINS_LINK) + set(CFL_ATOMIC_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}") + set(CMAKE_REQUIRED_LIBRARIES ${CFL_ATOMIC_REQUIRED_LIBRARIES}) + list(APPEND CMAKE_REQUIRED_LIBRARIES atomic) + + check_c_source_compiles("${CFL_ATOMIC_BUILTINS_LINK_SOURCE}" + CFL_ATOMIC_BUILTINS_LINK_WITH_LIBATOMIC) + + set(CMAKE_REQUIRED_LIBRARIES "${CFL_ATOMIC_REQUIRED_LIBRARIES}") + + if(CFL_ATOMIC_BUILTINS_LINK_WITH_LIBATOMIC) + set(CFL_ATOMIC_NEEDS_LIBATOMIC On) + elseif(CFL_SYSTEM_WINDOWS AND "${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") + set(PLATFORM_SPECIFIC_ATOMIC_MODULE cfl_atomic_msvc.c) + else() + set(PLATFORM_SPECIFIC_ATOMIC_MODULE cfl_atomic_generic.c) + set(CFL_ATOMIC_NEEDS_THREADS On) + endif() + endif() +endif() + +set(src + ${src} + ${PLATFORM_SPECIFIC_ATOMIC_MODULE} + ) + # Static Library add_library(cfl-static STATIC ${src}) -target_link_libraries(cfl-static xxhash) +target_link_libraries(cfl-static PRIVATE xxhash) + +if(CFL_ATOMIC_NEEDS_LIBATOMIC) + target_link_libraries(cfl-static PUBLIC atomic) +endif() + +if(CFL_ATOMIC_NEEDS_THREADS) + find_package(Threads REQUIRED) + target_link_libraries(cfl-static PUBLIC Threads::Threads) +endif() # Install Library if(MSVC) diff --git a/lib/cfl/src/cfl.c b/lib/cfl/src/cfl.c index 426319a2747..942d3c34065 100644 --- a/lib/cfl/src/cfl.c +++ b/lib/cfl/src/cfl.c @@ -21,11 +21,10 @@ int cfl_init() { - return 0; + return cfl_atomic_initialize(); } char *cfl_version() { return CFL_VERSION_STR; } - diff --git a/lib/cfl/src/cfl_array.c b/lib/cfl/src/cfl_array.c index 916b053e4af..af9d7cdc04a 100644 --- a/lib/cfl/src/cfl_array.c +++ b/lib/cfl/src/cfl_array.c @@ -21,9 +21,14 @@ #include #include +#include + +#include + struct cfl_array *cfl_array_create(size_t slot_count) { struct cfl_array *array; + size_t alloc_count; array = malloc(sizeof(struct cfl_array)); if (array == NULL) { @@ -35,7 +40,16 @@ struct cfl_array *cfl_array_create(size_t slot_count) array->resizable = CFL_FALSE; /* allocate fixed number of entries */ - array->entries = calloc(slot_count, sizeof(void *)); + alloc_count = slot_count; + if (alloc_count == 0) { + alloc_count = 1; + } + if (alloc_count > SIZE_MAX / sizeof(void *)) { + free(array); + return NULL; + } + + array->entries = calloc(alloc_count, sizeof(void *)); if (array->entries == NULL) { cfl_errno(); free(array); @@ -44,6 +58,9 @@ struct cfl_array *cfl_array_create(size_t slot_count) array->entry_count = 0; array->slot_count = slot_count; + array->owner = NULL; + array->parent_array = NULL; + array->parent_kvlist = NULL; return array; } @@ -70,6 +87,10 @@ void cfl_array_destroy(struct cfl_array *array) int cfl_array_resizable(struct cfl_array *array, int v) { + if (array == NULL) { + return -1; + } + if (v != CFL_TRUE && v != CFL_FALSE) { return -1; } @@ -81,6 +102,10 @@ int cfl_array_resizable(struct cfl_array *array, int v) int cfl_array_remove_by_index(struct cfl_array *array, size_t position) { + if (array == NULL) { + return -1; + } + if (position >= array->entry_count) { return -1; } @@ -105,6 +130,10 @@ int cfl_array_remove_by_reference(struct cfl_array *array, { size_t index; + if (array == NULL || value == NULL) { + return -1; + } + for (index = 0 ; index < array->entry_count ; index++) { if (array->entries[index] == value) { return cfl_array_remove_by_index(array, index); @@ -120,6 +149,11 @@ int cfl_array_append(struct cfl_array *array, void *tmp; size_t new_slot_count; size_t new_size; + size_t base_slot_count; + + if (array == NULL || value == NULL) { + return -1; + } if (array->entry_count >= array->slot_count) { /* @@ -129,17 +163,21 @@ int cfl_array_append(struct cfl_array *array, * it controls the input data. */ if (array->resizable) { - - /* - * if the array size is zero (created as an array of 0 slots), - * change the size to 1 so the resize can work properly - */ - if (array->slot_count == 0) { - array->slot_count = 1; + base_slot_count = array->slot_count; + if (base_slot_count == 0) { + base_slot_count = 1; } /* set new number of slots and total size */ - new_slot_count = (array->slot_count * 2); + if (base_slot_count > SIZE_MAX / 2) { + return -1; + } + + new_slot_count = (base_slot_count * 2); + if (new_slot_count > SIZE_MAX / sizeof(void *)) { + return -1; + } + new_size = (new_slot_count * sizeof(void *)); tmp = realloc(array->entries, new_size); @@ -160,6 +198,10 @@ int cfl_array_append(struct cfl_array *array, return -1; } + if (cfl_container_move_variant_to_array(array, value) != 0) { + return -1; + } + array->entries[array->entry_count++] = value; return 0; } @@ -360,6 +402,14 @@ int cfl_array_append_array(struct cfl_array *array, struct cfl_array *value) struct cfl_variant *value_instance; int result; + if (array == NULL || value == NULL) { + return -1; + } + + if (array == value) { + return -1; + } + value_instance = cfl_variant_create_from_array(value); if (value_instance == NULL) { @@ -368,6 +418,8 @@ int cfl_array_append_array(struct cfl_array *array, struct cfl_array *value) result = cfl_array_append(array, value_instance); if (result) { + cfl_container_release_variant(value_instance); + value_instance->data.as_array = NULL; cfl_variant_destroy(value_instance); return -2; } @@ -381,6 +433,10 @@ int cfl_array_append_new_array(struct cfl_array *array, size_t size) int result; struct cfl_array *value; + if (array == NULL) { + return -1; + } + value = cfl_array_create(size); if (value == NULL) { @@ -388,8 +444,7 @@ int cfl_array_append_new_array(struct cfl_array *array, size_t size) } result = cfl_array_append_array(array, value); - - if (result) { + if (result < 0) { cfl_array_destroy(value); } @@ -401,6 +456,10 @@ int cfl_array_append_kvlist(struct cfl_array *array, struct cfl_kvlist *value) struct cfl_variant *value_instance; int result; + if (array == NULL || value == NULL) { + return -1; + } + value_instance = cfl_variant_create_from_kvlist(value); if (value_instance == NULL) { return -1; @@ -408,6 +467,8 @@ int cfl_array_append_kvlist(struct cfl_array *array, struct cfl_kvlist *value) result = cfl_array_append(array, value_instance); if (result) { + cfl_container_release_variant(value_instance); + value_instance->data.as_kvlist = NULL; cfl_variant_destroy(value_instance); return -2; @@ -429,17 +490,36 @@ int cfl_array_print(FILE *fp, struct cfl_array *array) size = array->entry_count; if (size == 0) { - fputs("[]", fp); + if (fputs("[]", fp) == EOF) { + return -1; + } + return 0; } - fputs("[", fp); + if (fputc('[', fp) == EOF) { + return -1; + } + for (i=0; ientries[i]); - fputs(",", fp); + if (ret < 0) { + return -1; + } + + if (fputc(',', fp) == EOF) { + return -1; + } } + ret = cfl_variant_print(fp, array->entries[size-1]); - fputs("]", fp); + if (ret < 0) { + return -1; + } + + if (fputc(']', fp) == EOF) { + return -1; + } return ret; } diff --git a/lib/cfl/src/cfl_atomic_clang.c b/lib/cfl/src/cfl_atomic_clang.c new file mode 100644 index 00000000000..3c320ac2749 --- /dev/null +++ b/lib/cfl/src/cfl_atomic_clang.c @@ -0,0 +1,42 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CFL + * === + * Copyright (C) 2022 The CFL Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +int cfl_atomic_initialize() +{ + return 0; +} + +int cfl_atomic_compare_exchange(uint64_t *storage, + uint64_t old_value, uint64_t new_value) +{ + return __atomic_compare_exchange(storage, &old_value, &new_value, 0, + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} + +void cfl_atomic_store(uint64_t *storage, uint64_t new_value) +{ + __atomic_store_n(storage, new_value, __ATOMIC_SEQ_CST); +} + +uint64_t cfl_atomic_load(uint64_t *storage) +{ + return __atomic_load_n(storage, __ATOMIC_SEQ_CST); +} diff --git a/lib/cfl/src/cfl_atomic_gcc.c b/lib/cfl/src/cfl_atomic_gcc.c new file mode 100644 index 00000000000..3c320ac2749 --- /dev/null +++ b/lib/cfl/src/cfl_atomic_gcc.c @@ -0,0 +1,42 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CFL + * === + * Copyright (C) 2022 The CFL Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +int cfl_atomic_initialize() +{ + return 0; +} + +int cfl_atomic_compare_exchange(uint64_t *storage, + uint64_t old_value, uint64_t new_value) +{ + return __atomic_compare_exchange(storage, &old_value, &new_value, 0, + __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} + +void cfl_atomic_store(uint64_t *storage, uint64_t new_value) +{ + __atomic_store_n(storage, new_value, __ATOMIC_SEQ_CST); +} + +uint64_t cfl_atomic_load(uint64_t *storage) +{ + return __atomic_load_n(storage, __ATOMIC_SEQ_CST); +} diff --git a/lib/cfl/src/cfl_atomic_generic.c b/lib/cfl/src/cfl_atomic_generic.c new file mode 100644 index 00000000000..e59bbfa1209 --- /dev/null +++ b/lib/cfl/src/cfl_atomic_generic.c @@ -0,0 +1,121 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CFL + * === + * Copyright (C) 2022 The CFL Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +static pthread_mutex_t cfl_atomic_operation_lock; +static pthread_once_t cfl_atomic_operation_system_once = PTHREAD_ONCE_INIT; +static int cfl_atomic_operation_system_initialized = 0; +static int cfl_atomic_operation_system_status = 0; + +static void cfl_atomic_bootstrap() +{ + cfl_atomic_operation_system_status = + pthread_mutex_init(&cfl_atomic_operation_lock, NULL); + + if (cfl_atomic_operation_system_status == 0) { + cfl_atomic_operation_system_initialized = 1; + } +} + +int cfl_atomic_initialize() +{ + pthread_once(&cfl_atomic_operation_system_once, cfl_atomic_bootstrap); + + if (cfl_atomic_operation_system_status != 0) { + return 1; + } + + return 0; +} + +int cfl_atomic_compare_exchange(uint64_t *storage, + uint64_t old_value, uint64_t new_value) +{ + int result; + + if (cfl_atomic_initialize() != 0 || + cfl_atomic_operation_system_initialized == 0) { + return 0; + } + + result = pthread_mutex_lock(&cfl_atomic_operation_lock); + + if (result != 0) { + return 0; + } + + if (*storage == old_value) { + *storage = new_value; + + result = 1; + } + else { + result = 0; + } + + pthread_mutex_unlock(&cfl_atomic_operation_lock); + + return result; +} + +void cfl_atomic_store(uint64_t *storage, uint64_t new_value) +{ + int result; + + if (cfl_atomic_initialize() != 0 || + cfl_atomic_operation_system_initialized == 0) { + return; + } + + result = pthread_mutex_lock(&cfl_atomic_operation_lock); + + if (result != 0) { + return; + } + + *storage = new_value; + + pthread_mutex_unlock(&cfl_atomic_operation_lock); +} + +uint64_t cfl_atomic_load(uint64_t *storage) +{ + int result; + uint64_t retval; + + if (cfl_atomic_initialize() != 0 || + cfl_atomic_operation_system_initialized == 0) { + return 0; + } + + result = pthread_mutex_lock(&cfl_atomic_operation_lock); + + if (result != 0) { + return 0; + } + + retval = *storage; + + pthread_mutex_unlock(&cfl_atomic_operation_lock); + + return retval; +} diff --git a/lib/cfl/src/cfl_atomic_msvc.c b/lib/cfl/src/cfl_atomic_msvc.c new file mode 100644 index 00000000000..a900420ee95 --- /dev/null +++ b/lib/cfl/src/cfl_atomic_msvc.c @@ -0,0 +1,160 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CFL + * === + * Copyright (C) 2022 The CFL Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#include +#undef WIN32_LEAN_AND_MEAN +#else +#include +#endif + +#ifdef _WIN64 +#include +#endif + +#ifndef _WIN64 +static CRITICAL_SECTION cfl_atomic_operation_lock; +static INIT_ONCE cfl_atomic_operation_system_once = INIT_ONCE_STATIC_INIT; +static int cfl_atomic_operation_system_initialized = 0; +static int cfl_atomic_operation_system_status = 0; + +static BOOL CALLBACK cfl_atomic_bootstrap(PINIT_ONCE once, PVOID parameter, + PVOID *context) +{ + (void) once; + (void) parameter; + (void) context; + + InitializeCriticalSection(&cfl_atomic_operation_lock); + cfl_atomic_operation_system_initialized = 1; + + return TRUE; +} + +int cfl_atomic_initialize() +{ + if (!InitOnceExecuteOnce(&cfl_atomic_operation_system_once, + cfl_atomic_bootstrap, NULL, NULL)) { + cfl_atomic_operation_system_status = 1; + return 1; + } + + cfl_atomic_operation_system_status = 0; + + return 0; +} + +int cfl_atomic_compare_exchange(uint64_t *storage, + uint64_t old_value, uint64_t new_value) +{ + int result; + + if (cfl_atomic_initialize() != 0 || + cfl_atomic_operation_system_initialized == 0 || + cfl_atomic_operation_system_status != 0) { + return 0; + } + + EnterCriticalSection(&cfl_atomic_operation_lock); + + if (*storage == old_value) { + *storage = new_value; + + result = 1; + } + else { + result = 0; + } + + LeaveCriticalSection(&cfl_atomic_operation_lock); + + return result; +} + +void cfl_atomic_store(uint64_t *storage, uint64_t new_value) +{ + if (cfl_atomic_initialize() != 0 || + cfl_atomic_operation_system_initialized == 0 || + cfl_atomic_operation_system_status != 0) { + return; + } + + EnterCriticalSection(&cfl_atomic_operation_lock); + + *storage = new_value; + + LeaveCriticalSection(&cfl_atomic_operation_lock); +} + +uint64_t cfl_atomic_load(uint64_t *storage) +{ + uint64_t result; + + if (cfl_atomic_initialize() != 0 || + cfl_atomic_operation_system_initialized == 0 || + cfl_atomic_operation_system_status != 0) { + return 0; + } + + EnterCriticalSection(&cfl_atomic_operation_lock); + + result = *storage; + + LeaveCriticalSection(&cfl_atomic_operation_lock); + + return result; +} + +#else /* _WIN64 */ + +int cfl_atomic_initialize() +{ + return 0; +} + +int cfl_atomic_compare_exchange(uint64_t *storage, + uint64_t old_value, uint64_t new_value) +{ + __int64 result; + + result = _InterlockedCompareExchange64((volatile __int64 *) storage, + (__int64) new_value, + (__int64) old_value); + + if ((uint64_t) result != old_value) { + return 0; + } + + return 1; +} + +void cfl_atomic_store(uint64_t *storage, uint64_t new_value) +{ + _InterlockedExchange64((volatile __int64 *) storage, (__int64) new_value); +} + +uint64_t cfl_atomic_load(uint64_t *storage) +{ + return (uint64_t) _InterlockedOr64((volatile __int64 *) storage, 0); +} + +#endif diff --git a/lib/cfl/src/cfl_checksum.c b/lib/cfl/src/cfl_checksum.c index 9b0297eaf02..d51acc65ffb 100644 --- a/lib/cfl/src/cfl_checksum.c +++ b/lib/cfl/src/cfl_checksum.c @@ -93,6 +93,10 @@ uint32_t cfl_checksum_crc32c(unsigned char *buffer, size_t length) uint32_t checksum; size_t index; + if (buffer == NULL && length > 0) { + return 0; + } + checksum = 0xFFFFFFFF; /* Keeping in mind that compilers are smart enough to optimize these diff --git a/lib/cfl/src/cfl_container.c b/lib/cfl/src/cfl_container.c new file mode 100644 index 00000000000..41fb48436fc --- /dev/null +++ b/lib/cfl/src/cfl_container.c @@ -0,0 +1,601 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CFL + * === + * Copyright (C) 2026 The CFL Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#define CFL_CONTAINER_MAX_DEPTH 512 + +static int variant_contains_array(struct cfl_variant *variant, + struct cfl_array *target, + size_t depth); +static int variant_contains_kvlist(struct cfl_variant *variant, + struct cfl_kvlist *target, + size_t depth); +static int variant_contains_variant(struct cfl_variant *variant, + struct cfl_variant *target, + size_t depth); + +static int depth_exceeded(size_t depth) +{ + if (depth > CFL_CONTAINER_MAX_DEPTH) { + return CFL_TRUE; + } + + return CFL_FALSE; +} + +static int array_contains_array(struct cfl_array *array, + struct cfl_array *target, + size_t depth) +{ + size_t i; + + if (array == NULL || target == NULL) { + return CFL_FALSE; + } + + if (depth_exceeded(depth)) { + return CFL_TRUE; + } + + if (array == target) { + return CFL_TRUE; + } + + for (i = 0; i < array->entry_count; i++) { + if (variant_contains_array(array->entries[i], target, depth + 1)) { + return CFL_TRUE; + } + } + + return CFL_FALSE; +} + +static int array_contains_kvlist(struct cfl_array *array, + struct cfl_kvlist *target, + size_t depth) +{ + size_t i; + + if (array == NULL || target == NULL) { + return CFL_FALSE; + } + + if (depth_exceeded(depth)) { + return CFL_TRUE; + } + + for (i = 0; i < array->entry_count; i++) { + if (variant_contains_kvlist(array->entries[i], target, depth + 1)) { + return CFL_TRUE; + } + } + + return CFL_FALSE; +} + +static int array_contains_variant(struct cfl_array *array, + struct cfl_variant *target, + size_t depth) +{ + size_t i; + + if (array == NULL || target == NULL) { + return CFL_FALSE; + } + + if (depth_exceeded(depth)) { + return CFL_TRUE; + } + + for (i = 0; i < array->entry_count; i++) { + if (variant_contains_variant(array->entries[i], target, depth + 1)) { + return CFL_TRUE; + } + } + + return CFL_FALSE; +} + +static int kvlist_contains_array(struct cfl_kvlist *kvlist, + struct cfl_array *target, + size_t depth) +{ + struct cfl_list *head; + struct cfl_kvpair *pair; + + if (kvlist == NULL || target == NULL) { + return CFL_FALSE; + } + + if (depth_exceeded(depth)) { + return CFL_TRUE; + } + + cfl_list_foreach(head, &kvlist->list) { + pair = cfl_list_entry(head, struct cfl_kvpair, _head); + + if (pair != NULL && variant_contains_array(pair->val, target, depth + 1)) { + return CFL_TRUE; + } + } + + return CFL_FALSE; +} + +static int kvlist_contains_kvlist(struct cfl_kvlist *kvlist, + struct cfl_kvlist *target, + size_t depth) +{ + struct cfl_list *head; + struct cfl_kvpair *pair; + + if (kvlist == NULL || target == NULL) { + return CFL_FALSE; + } + + if (depth_exceeded(depth)) { + return CFL_TRUE; + } + + if (kvlist == target) { + return CFL_TRUE; + } + + cfl_list_foreach(head, &kvlist->list) { + pair = cfl_list_entry(head, struct cfl_kvpair, _head); + + if (pair != NULL && variant_contains_kvlist(pair->val, target, depth + 1)) { + return CFL_TRUE; + } + } + + return CFL_FALSE; +} + +static int kvlist_contains_variant(struct cfl_kvlist *kvlist, + struct cfl_variant *target, + size_t depth) +{ + struct cfl_list *head; + struct cfl_kvpair *pair; + + if (kvlist == NULL || target == NULL) { + return CFL_FALSE; + } + + if (depth_exceeded(depth)) { + return CFL_TRUE; + } + + cfl_list_foreach(head, &kvlist->list) { + pair = cfl_list_entry(head, struct cfl_kvpair, _head); + + if (pair != NULL && variant_contains_variant(pair->val, target, depth + 1)) { + return CFL_TRUE; + } + } + + return CFL_FALSE; +} + +static int variant_contains_array(struct cfl_variant *variant, + struct cfl_array *target, + size_t depth) +{ + if (variant == NULL || target == NULL) { + return CFL_FALSE; + } + + if (depth_exceeded(depth)) { + return CFL_TRUE; + } + + if (variant->type == CFL_VARIANT_ARRAY) { + return array_contains_array(variant->data.as_array, target, depth + 1); + } + + if (variant->type == CFL_VARIANT_KVLIST) { + return kvlist_contains_array(variant->data.as_kvlist, target, depth + 1); + } + + return CFL_FALSE; +} + +static int variant_contains_kvlist(struct cfl_variant *variant, + struct cfl_kvlist *target, + size_t depth) +{ + if (variant == NULL || target == NULL) { + return CFL_FALSE; + } + + if (depth_exceeded(depth)) { + return CFL_TRUE; + } + + if (variant->type == CFL_VARIANT_ARRAY) { + return array_contains_kvlist(variant->data.as_array, target, depth + 1); + } + + if (variant->type == CFL_VARIANT_KVLIST) { + return kvlist_contains_kvlist(variant->data.as_kvlist, target, depth + 1); + } + + return CFL_FALSE; +} + +static int variant_contains_variant(struct cfl_variant *variant, + struct cfl_variant *target, + size_t depth) +{ + if (variant == NULL || target == NULL) { + return CFL_FALSE; + } + + if (depth_exceeded(depth)) { + return CFL_TRUE; + } + + if (variant == target) { + return CFL_TRUE; + } + + if (variant->type == CFL_VARIANT_ARRAY) { + return array_contains_variant(variant->data.as_array, target, depth + 1); + } + + if (variant->type == CFL_VARIANT_KVLIST) { + return kvlist_contains_variant(variant->data.as_kvlist, target, depth + 1); + } + + return CFL_FALSE; +} + +int cfl_container_array_contains_array(struct cfl_array *array, + struct cfl_array *target) +{ + return array_contains_array(array, target, 0); +} + +int cfl_container_array_contains_kvlist(struct cfl_array *array, + struct cfl_kvlist *target) +{ + return array_contains_kvlist(array, target, 0); +} + +int cfl_container_array_contains_variant(struct cfl_array *array, + struct cfl_variant *target) +{ + return array_contains_variant(array, target, 0); +} + +int cfl_container_kvlist_contains_array(struct cfl_kvlist *kvlist, + struct cfl_array *target) +{ + return kvlist_contains_array(kvlist, target, 0); +} + +int cfl_container_kvlist_contains_kvlist(struct cfl_kvlist *kvlist, + struct cfl_kvlist *target) +{ + return kvlist_contains_kvlist(kvlist, target, 0); +} + +int cfl_container_kvlist_contains_variant(struct cfl_kvlist *kvlist, + struct cfl_variant *target) +{ + return kvlist_contains_variant(kvlist, target, 0); +} + +int cfl_container_variant_contains_array(struct cfl_variant *variant, + struct cfl_array *target) +{ + return variant_contains_array(variant, target, 0); +} + +int cfl_container_variant_contains_kvlist(struct cfl_variant *variant, + struct cfl_kvlist *target) +{ + return variant_contains_kvlist(variant, target, 0); +} + +int cfl_container_variant_contains_variant(struct cfl_variant *variant, + struct cfl_variant *target) +{ + return variant_contains_variant(variant, target, 0); +} + +static int parent_chain_contains_array(struct cfl_array *array, + struct cfl_kvlist *kvlist, + struct cfl_array *target) +{ + struct cfl_array *next_array; + struct cfl_kvlist *next_kvlist; + size_t depth; + + depth = 0; + + while (array != NULL || kvlist != NULL) { + if (depth_exceeded(depth)) { + return CFL_TRUE; + } + + next_array = NULL; + next_kvlist = NULL; + + if (array != NULL) { + if (array == target) { + return CFL_TRUE; + } + + next_array = array->parent_array; + next_kvlist = array->parent_kvlist; + } + else { + next_array = kvlist->parent_array; + next_kvlist = kvlist->parent_kvlist; + } + + array = next_array; + kvlist = next_kvlist; + depth++; + } + + return CFL_FALSE; +} + +static int parent_chain_contains_kvlist(struct cfl_array *array, + struct cfl_kvlist *kvlist, + struct cfl_kvlist *target) +{ + struct cfl_array *next_array; + struct cfl_kvlist *next_kvlist; + size_t depth; + + depth = 0; + + while (array != NULL || kvlist != NULL) { + if (depth_exceeded(depth)) { + return CFL_TRUE; + } + + next_array = NULL; + next_kvlist = NULL; + + if (kvlist != NULL) { + if (kvlist == target) { + return CFL_TRUE; + } + + next_array = kvlist->parent_array; + next_kvlist = kvlist->parent_kvlist; + } + else { + next_array = array->parent_array; + next_kvlist = array->parent_kvlist; + } + + array = next_array; + kvlist = next_kvlist; + depth++; + } + + return CFL_FALSE; +} + +static int claim_variant_container(struct cfl_variant *variant) +{ + if (variant->type == CFL_VARIANT_ARRAY) { + if (variant->data.as_array != NULL && + cfl_container_claim_array(variant->data.as_array, variant) != 0) { + return -1; + } + } + + if (variant->type == CFL_VARIANT_KVLIST) { + if (variant->data.as_kvlist != NULL && + cfl_container_claim_kvlist(variant->data.as_kvlist, variant) != 0) { + return -1; + } + } + + return 0; +} + +int cfl_container_claim_array(struct cfl_array *array, + struct cfl_variant *owner) +{ + if (array == NULL || owner == NULL) { + return -1; + } + + if (array->owner != NULL && array->owner != owner) { + return -1; + } + + array->owner = owner; + + return 0; +} + +int cfl_container_claim_kvlist(struct cfl_kvlist *kvlist, + struct cfl_variant *owner) +{ + if (kvlist == NULL || owner == NULL) { + return -1; + } + + if (kvlist->owner != NULL && kvlist->owner != owner) { + return -1; + } + + kvlist->owner = owner; + + return 0; +} + +int cfl_container_adopt_variant(struct cfl_variant *variant) +{ + if (variant == NULL) { + return -1; + } + + if (variant->owned) { + return -1; + } + + if (claim_variant_container(variant) != 0) { + return -1; + } + + variant->owned = CFL_TRUE; + + return 0; +} + +int cfl_container_move_variant_to_array(struct cfl_array *array, + struct cfl_variant *variant) +{ + struct cfl_array *child_array; + struct cfl_kvlist *child_kvlist; + + if (array == NULL || variant == NULL) { + return -1; + } + + if (variant->owned) { + return -1; + } + + if (variant->type == CFL_VARIANT_ARRAY) { + child_array = variant->data.as_array; + + if (child_array != NULL && + parent_chain_contains_array(array, NULL, child_array)) { + return -1; + } + } + else if (variant->type == CFL_VARIANT_KVLIST) { + child_kvlist = variant->data.as_kvlist; + + if (child_kvlist != NULL && + parent_chain_contains_kvlist(array, NULL, child_kvlist)) { + return -1; + } + } + + if (claim_variant_container(variant) != 0) { + return -1; + } + + if (variant->type == CFL_VARIANT_ARRAY && + variant->data.as_array != NULL) { + variant->data.as_array->parent_array = array; + variant->data.as_array->parent_kvlist = NULL; + } + else if (variant->type == CFL_VARIANT_KVLIST && + variant->data.as_kvlist != NULL) { + variant->data.as_kvlist->parent_array = array; + variant->data.as_kvlist->parent_kvlist = NULL; + } + + variant->owned = CFL_TRUE; + + return 0; +} + +int cfl_container_move_variant_to_kvlist(struct cfl_kvlist *kvlist, + struct cfl_variant *variant) +{ + struct cfl_array *child_array; + struct cfl_kvlist *child_kvlist; + + if (kvlist == NULL || variant == NULL) { + return -1; + } + + if (variant->owned) { + return -1; + } + + if (variant->type == CFL_VARIANT_ARRAY) { + child_array = variant->data.as_array; + + if (child_array != NULL && + parent_chain_contains_array(NULL, kvlist, child_array)) { + return -1; + } + } + else if (variant->type == CFL_VARIANT_KVLIST) { + child_kvlist = variant->data.as_kvlist; + + if (child_kvlist != NULL && + parent_chain_contains_kvlist(NULL, kvlist, child_kvlist)) { + return -1; + } + } + + if (claim_variant_container(variant) != 0) { + return -1; + } + + if (variant->type == CFL_VARIANT_ARRAY && + variant->data.as_array != NULL) { + variant->data.as_array->parent_array = NULL; + variant->data.as_array->parent_kvlist = kvlist; + } + else if (variant->type == CFL_VARIANT_KVLIST && + variant->data.as_kvlist != NULL) { + variant->data.as_kvlist->parent_array = NULL; + variant->data.as_kvlist->parent_kvlist = kvlist; + } + + variant->owned = CFL_TRUE; + + return 0; +} + +void cfl_container_release_variant(struct cfl_variant *variant) +{ + if (variant == NULL) { + return; + } + + variant->owned = CFL_FALSE; + + if (variant->type == CFL_VARIANT_ARRAY) { + if (variant->data.as_array != NULL && + variant->data.as_array->owner == variant) { + variant->data.as_array->owner = NULL; + variant->data.as_array->parent_array = NULL; + variant->data.as_array->parent_kvlist = NULL; + } + } + else if (variant->type == CFL_VARIANT_KVLIST) { + if (variant->data.as_kvlist != NULL && + variant->data.as_kvlist->owner == variant) { + variant->data.as_kvlist->owner = NULL; + variant->data.as_kvlist->parent_array = NULL; + variant->data.as_kvlist->parent_kvlist = NULL; + } + } +} diff --git a/lib/cfl/src/cfl_kv.c b/lib/cfl/src/cfl_kv.c index 23d3c4d958c..7368581443b 100644 --- a/lib/cfl/src/cfl_kv.c +++ b/lib/cfl/src/cfl_kv.c @@ -21,9 +21,14 @@ #include #include +#include void cfl_kv_init(struct cfl_list *list) { + if (list == NULL) { + return; + } + cfl_list_init(list); } @@ -33,6 +38,14 @@ struct cfl_kv *cfl_kv_item_create_len(struct cfl_list *list, { struct cfl_kv *kv; + if (list == NULL || k_buf == NULL || k_len > INT_MAX || v_len > INT_MAX) { + return NULL; + } + + if (v_len > 0 && v_buf == NULL) { + return NULL; + } + kv = calloc(1, sizeof(struct cfl_kv)); if (kv == NULL) { @@ -41,7 +54,7 @@ struct cfl_kv *cfl_kv_item_create_len(struct cfl_list *list, return NULL; } - kv->key = cfl_sds_create_len(k_buf, k_len); + kv->key = cfl_sds_create_len(k_buf, (int) k_len); if (kv->key == NULL) { free(kv); @@ -50,7 +63,7 @@ struct cfl_kv *cfl_kv_item_create_len(struct cfl_list *list, } if (v_len > 0) { - kv->val = cfl_sds_create_len(v_buf, v_len); + kv->val = cfl_sds_create_len(v_buf, (int) v_len); if (kv->val == NULL) { cfl_sds_destroy(kv->key); @@ -68,10 +81,10 @@ struct cfl_kv *cfl_kv_item_create_len(struct cfl_list *list, struct cfl_kv *cfl_kv_item_create(struct cfl_list *list, char *k_buf, char *v_buf) { - int k_len; - int v_len; + size_t k_len; + size_t v_len; - if (k_buf == NULL) { + if (list == NULL || k_buf == NULL) { return NULL; } @@ -84,11 +97,19 @@ struct cfl_kv *cfl_kv_item_create(struct cfl_list *list, v_len = 0; } + if (k_len > INT_MAX || v_len > INT_MAX) { + return NULL; + } + return cfl_kv_item_create_len(list, k_buf, k_len, v_buf, v_len); } void cfl_kv_item_destroy(struct cfl_kv *kv) { + if (kv == NULL) { + return; + } + if (kv->key != NULL) { cfl_sds_destroy(kv->key); } @@ -108,6 +129,10 @@ void cfl_kv_release(struct cfl_list *list) struct cfl_list *tmp; struct cfl_kv *kv; + if (list == NULL) { + return; + } + cfl_list_foreach_safe(head, tmp, list) { kv = cfl_list_entry(head, struct cfl_kv, _head); @@ -118,10 +143,10 @@ void cfl_kv_release(struct cfl_list *list) const char *cfl_kv_get_key_value(const char *key, struct cfl_list *list) { struct cfl_list *head; - int len; + size_t len; struct cfl_kv *kv; - if (key == NULL) { + if (key == NULL || list == NULL) { return NULL; } diff --git a/lib/cfl/src/cfl_kvlist.c b/lib/cfl/src/cfl_kvlist.c index 4ffc5ea83a2..9d1bb18a597 100644 --- a/lib/cfl/src/cfl_kvlist.c +++ b/lib/cfl/src/cfl_kvlist.c @@ -23,6 +23,67 @@ #include #include +#include + +#include + +static int print_json_string(FILE *fp, const char *str, size_t len) +{ + size_t i; + unsigned char c; + int ret; + + if (fputc('"', fp) == EOF) { + return -1; + } + + for (i = 0; i < len; i++) { + c = (unsigned char) str[i]; + + switch (c) { + case '"': + ret = fputs("\\\"", fp); + break; + case '\\': + ret = fputs("\\\\", fp); + break; + case '\b': + ret = fputs("\\b", fp); + break; + case '\f': + ret = fputs("\\f", fp); + break; + case '\n': + ret = fputs("\\n", fp); + break; + case '\r': + ret = fputs("\\r", fp); + break; + case '\t': + ret = fputs("\\t", fp); + break; + default: + if (c < 0x20) { + ret = fprintf(fp, "\\u%04x", c); + } + else { + ret = fputc(c, fp); + } + break; + } + + if (ret < 0) { + return -1; + } + } + + if (fputc('"', fp) == EOF) { + return -1; + } + + return 0; +} + struct cfl_kvlist *cfl_kvlist_create() { struct cfl_kvlist *list; @@ -34,6 +95,10 @@ struct cfl_kvlist *cfl_kvlist_create() } cfl_list_init(&list->list); + list->owner = NULL; + list->parent_array = NULL; + list->parent_kvlist = NULL; + return list; } @@ -43,6 +108,10 @@ void cfl_kvlist_destroy(struct cfl_kvlist *list) struct cfl_list *head; struct cfl_kvpair *pair; + if (list == NULL) { + return; + } + cfl_list_foreach_safe(head, tmp, &list->list) { pair = cfl_list_entry(head, struct cfl_kvpair, _head); @@ -68,6 +137,10 @@ int cfl_kvlist_insert_string_s(struct cfl_kvlist *list, struct cfl_variant *value_instance; int result; + if (list == NULL || key == NULL || key_size > INT_MAX) { + return -1; + } + value_instance = cfl_variant_create_from_string_s(value, value_size, referenced); if (value_instance == NULL) { return -1; @@ -91,6 +164,10 @@ int cfl_kvlist_insert_bytes_s(struct cfl_kvlist *list, struct cfl_variant *value_instance; int result; + if (list == NULL || key == NULL || key_size > INT_MAX) { + return -1; + } + value_instance = cfl_variant_create_from_bytes(value, length, referenced); if (value_instance == NULL) { return -1; @@ -112,6 +189,10 @@ int cfl_kvlist_insert_reference_s(struct cfl_kvlist *list, struct cfl_variant *value_instance; int result; + if (list == NULL || key == NULL || key_size > INT_MAX) { + return -1; + } + value_instance = cfl_variant_create_from_reference(value); if (value_instance == NULL) { @@ -135,6 +216,10 @@ int cfl_kvlist_insert_bool_s(struct cfl_kvlist *list, struct cfl_variant *value_instance; int result; + if (list == NULL || key == NULL || key_size > INT_MAX) { + return -1; + } + value_instance = cfl_variant_create_from_bool(value); if (value_instance == NULL) { @@ -158,6 +243,10 @@ int cfl_kvlist_insert_int64_s(struct cfl_kvlist *list, struct cfl_variant *value_instance; int result; + if (list == NULL || key == NULL || key_size > INT_MAX) { + return -1; + } + value_instance = cfl_variant_create_from_int64(value); if (value_instance == NULL) { @@ -181,6 +270,10 @@ int cfl_kvlist_insert_uint64_s(struct cfl_kvlist *list, struct cfl_variant *value_instance; int result; + if (list == NULL || key == NULL || key_size > INT_MAX) { + return -1; + } + value_instance = cfl_variant_create_from_uint64(value); if (value_instance == NULL) { @@ -204,6 +297,10 @@ int cfl_kvlist_insert_double_s(struct cfl_kvlist *list, struct cfl_variant *value_instance; int result; + if (list == NULL || key == NULL || key_size > INT_MAX) { + return -1; + } + value_instance = cfl_variant_create_from_double(value); if (value_instance == NULL) { @@ -227,6 +324,10 @@ int cfl_kvlist_insert_array_s(struct cfl_kvlist *list, struct cfl_variant *value_instance; int result; + if (list == NULL || key == NULL || key_size > INT_MAX || value == NULL) { + return -1; + } + value_instance = cfl_variant_create_from_array(value); if (value_instance == NULL) { @@ -236,6 +337,8 @@ int cfl_kvlist_insert_array_s(struct cfl_kvlist *list, result = cfl_kvlist_insert_s(list, key, key_size, value_instance); if (result) { + cfl_container_release_variant(value_instance); + value_instance->data.as_array = NULL; cfl_variant_destroy(value_instance); return -2; @@ -251,6 +354,10 @@ int cfl_kvlist_insert_new_array_s(struct cfl_kvlist *list, int result; struct cfl_array *value; + if (list == NULL || key == NULL || key_size > INT_MAX) { + return -1; + } + value = cfl_array_create(size); if (value == NULL) { @@ -258,8 +365,7 @@ int cfl_kvlist_insert_new_array_s(struct cfl_kvlist *list, } result = cfl_kvlist_insert_array_s(list, key, key_size, value); - - if (result) { + if (result < 0) { cfl_array_destroy(value); } @@ -272,6 +378,14 @@ int cfl_kvlist_insert_kvlist_s(struct cfl_kvlist *list, struct cfl_variant *value_instance; int result; + if (list == NULL || key == NULL || key_size > INT_MAX || value == NULL) { + return -1; + } + + if (list == value) { + return -1; + } + value_instance = cfl_variant_create_from_kvlist(value); if (value_instance == NULL) { return -1; @@ -280,6 +394,8 @@ int cfl_kvlist_insert_kvlist_s(struct cfl_kvlist *list, result = cfl_kvlist_insert_s(list, key, key_size, value_instance); if (result) { + cfl_container_release_variant(value_instance); + value_instance->data.as_kvlist = NULL; cfl_variant_destroy(value_instance); return -2; @@ -294,7 +410,7 @@ int cfl_kvlist_insert_s(struct cfl_kvlist *list, { struct cfl_kvpair *pair; - if (list == NULL || key == NULL || value == NULL) { + if (list == NULL || key == NULL || value == NULL || key_size > INT_MAX) { return -1; } @@ -304,13 +420,20 @@ int cfl_kvlist_insert_s(struct cfl_kvlist *list, return -1; } - pair->key = cfl_sds_create_len(key, key_size); + pair->key = cfl_sds_create_len(key, (int) key_size); if (pair->key == NULL) { free(pair); return -2; } + if (cfl_container_move_variant_to_kvlist(list, value) != 0) { + cfl_sds_destroy(pair->key); + free(pair); + + return -1; + } + pair->val = value; cfl_list_add(&pair->_head, &list->list); @@ -322,6 +445,10 @@ struct cfl_variant *cfl_kvlist_fetch_s(struct cfl_kvlist *list, char *key, size_ struct cfl_list *head; struct cfl_kvpair *pair; + if (list == NULL || key == NULL) { + return NULL; + } + cfl_list_foreach(head, &list->list) { pair = cfl_list_entry(head, struct cfl_kvpair, _head); @@ -341,15 +468,18 @@ struct cfl_variant *cfl_kvlist_fetch_s(struct cfl_kvlist *list, char *key, size_ int cfl_kvlist_insert_string(struct cfl_kvlist *list, char *key, char *value) { - int key_len; - int val_len; + size_t key_len; + size_t val_len; - if (!key || !value) { + if (!list || !key || !value) { return -1; } key_len = strlen(key); val_len = strlen(value); + if (key_len > INT_MAX || val_len > INT_MAX) { + return -1; + } return cfl_kvlist_insert_string_s(list, key, key_len, value, val_len, CFL_FALSE); } @@ -358,78 +488,126 @@ int cfl_kvlist_insert_bytes(struct cfl_kvlist *list, char *key, char *value, size_t length, int referenced) { + if (!list || !key || (value == NULL && length > 0)) { + return -1; + } + return cfl_kvlist_insert_bytes_s(list, key, strlen(key), value, length, referenced); } int cfl_kvlist_insert_reference(struct cfl_kvlist *list, char *key, void *value) { + if (!list || !key) { + return -1; + } + return cfl_kvlist_insert_reference_s(list, key, strlen(key), value); } int cfl_kvlist_insert_bool(struct cfl_kvlist *list, char *key, int value) { + if (!list || !key) { + return -1; + } + return cfl_kvlist_insert_bool_s(list, key, strlen(key), value); } int cfl_kvlist_insert_int64(struct cfl_kvlist *list, char *key, int64_t value) { + if (!list || !key) { + return -1; + } + return cfl_kvlist_insert_int64_s(list, key, strlen(key), value); } int cfl_kvlist_insert_uint64(struct cfl_kvlist *list, char *key, uint64_t value) { + if (!list || !key) { + return -1; + } + return cfl_kvlist_insert_uint64_s(list, key, strlen(key), value); } int cfl_kvlist_insert_double(struct cfl_kvlist *list, char *key, double value) { + if (!list || !key) { + return -1; + } + return cfl_kvlist_insert_double_s(list, key, strlen(key), value); } int cfl_kvlist_insert_array(struct cfl_kvlist *list, char *key, struct cfl_array *value) { + if (!list || !key || !value) { + return -1; + } + return cfl_kvlist_insert_array_s(list, key, strlen(key), value); } int cfl_kvlist_insert_new_array(struct cfl_kvlist *list, char *key, size_t size) { + if (!list || !key) { + return -1; + } + return cfl_kvlist_insert_new_array_s(list, key, strlen(key), size); } int cfl_kvlist_insert_kvlist(struct cfl_kvlist *list, char *key, struct cfl_kvlist *value) { + if (!list || !key || !value) { + return -1; + } + return cfl_kvlist_insert_kvlist_s(list, key, strlen(key), value); } int cfl_kvlist_insert(struct cfl_kvlist *list, char *key, struct cfl_variant *value) { + if (!list || !key || !value) { + return -1; + } + return cfl_kvlist_insert_s(list, key, strlen(key), value); } struct cfl_variant *cfl_kvlist_fetch(struct cfl_kvlist *list, char *key) { + if (!list || !key) { + return NULL; + } + return cfl_kvlist_fetch_s(list, key, strlen(key)); } int cfl_kvlist_count(struct cfl_kvlist *list) { + if (list == NULL) { + return 0; + } + return cfl_list_size(&list->list); } int cfl_kvlist_print(FILE *fp, struct cfl_kvlist *list) { - size_t size; - size_t i; - int ret = -1; + size_t key_size; + int printed; + int ret = 0; struct cfl_list *head = NULL; struct cfl_kvpair *pair = NULL; @@ -438,24 +616,44 @@ int cfl_kvlist_print(FILE *fp, struct cfl_kvlist *list) return -1; } - size = (size_t)cfl_kvlist_count(list); - i = 0; - fputs("{", fp); + printed = CFL_FALSE; + if (fputc('{', fp) == EOF) { + return -1; + } + cfl_list_foreach(head, &list->list) { pair = cfl_list_entry(head, struct cfl_kvpair, _head); if (pair == NULL || pair->key == NULL || pair->val == NULL) { continue; } - fprintf(fp, "\"%s\":", pair->key); - ret = cfl_variant_print(fp, pair->val); + if (printed) { + if (fputc(',', fp) == EOF) { + return -1; + } + } + + key_size = cfl_sds_len(pair->key); + ret = print_json_string(fp, pair->key, key_size); + if (ret < 0) { + return -1; + } + + if (fputc(':', fp) == EOF) { + return -1; + } - i++; - if (i != size) { - fputs(",", fp); + ret = cfl_variant_print(fp, pair->val); + if (ret < 0) { + return -1; } + + printed = CFL_TRUE; + } + + if (fputc('}', fp) == EOF) { + return -1; } - fputs("}", fp); return ret; } @@ -464,12 +662,25 @@ int cfl_kvlist_contains(struct cfl_kvlist *kvlist, char *name) { struct cfl_list *iterator; struct cfl_kvpair *pair; + size_t name_len; + size_t key_len; + + if (kvlist == NULL || name == NULL) { + return CFL_FALSE; + } + + name_len = strlen(name); cfl_list_foreach(iterator, &kvlist->list) { pair = cfl_list_entry(iterator, struct cfl_kvpair, _head); - if (strcasecmp(pair->key, name) == 0) { + key_len = cfl_sds_len(pair->key); + if (key_len != name_len) { + continue; + } + + if (strncasecmp(pair->key, name, name_len) == 0) { return CFL_TRUE; } } @@ -483,17 +694,33 @@ int cfl_kvlist_remove(struct cfl_kvlist *kvlist, char *name) struct cfl_list *iterator_backup; struct cfl_list *iterator; struct cfl_kvpair *pair; + size_t name_len; + size_t key_len; + int removed; + + if (kvlist == NULL || name == NULL) { + return CFL_FALSE; + } + + name_len = strlen(name); + removed = CFL_FALSE; cfl_list_foreach_safe(iterator, iterator_backup, &kvlist->list) { pair = cfl_list_entry(iterator, struct cfl_kvpair, _head); - if (strcasecmp(pair->key, name) == 0) { + key_len = cfl_sds_len(pair->key); + if (key_len != name_len) { + continue; + } + + if (strncasecmp(pair->key, name, name_len) == 0) { cfl_kvpair_destroy(pair); + removed = CFL_TRUE; } } - return CFL_TRUE; + return removed; } @@ -516,3 +743,18 @@ void cfl_kvpair_destroy(struct cfl_kvpair *pair) } } +struct cfl_variant *cfl_kvpair_take_value(struct cfl_kvpair *pair) +{ + struct cfl_variant *value; + + if (pair == NULL) { + return NULL; + } + + value = pair->val; + pair->val = NULL; + + cfl_container_release_variant(value); + + return value; +} diff --git a/lib/cfl/src/cfl_object.c b/lib/cfl/src/cfl_object.c index 56d13ee2ea8..199b62bd68b 100644 --- a/lib/cfl/src/cfl_object.c +++ b/lib/cfl/src/cfl_object.c @@ -18,6 +18,7 @@ */ #include "cfl/cfl.h" +#include /* CFL Object * ========== @@ -43,38 +44,95 @@ struct cfl_object *cfl_object_create() return o; } +static int reuses_current_root(struct cfl_object *o, int type, void *ptr) +{ + if (o->variant == NULL) { + return CFL_FALSE; + } + + if (type == CFL_OBJECT_KVLIST && + o->variant->type == CFL_VARIANT_KVLIST && + o->variant->data.as_kvlist == ptr) { + return CFL_TRUE; + } + + if (type == CFL_OBJECT_ARRAY && + o->variant->type == CFL_VARIANT_ARRAY && + o->variant->data.as_array == ptr) { + return CFL_TRUE; + } + + if (type == CFL_OBJECT_VARIANT && o->variant == ptr) { + return CFL_TRUE; + } + + return CFL_FALSE; +} + /* * Associate a CFL data type to the object. We only support kvlist, array and variant. Note * that everything is held as a variant internally. */ int cfl_object_set(struct cfl_object *o, int type, void *ptr) { - if (!o) { + struct cfl_variant *variant; + int variant_created; + + if (!o || !ptr) { return -1; } + if (o->variant != NULL) { + if (reuses_current_root(o, type, ptr)) { + o->type = type; + return 0; + } + } + + variant_created = CFL_FALSE; + if (type == CFL_OBJECT_KVLIST) { - o->type = CFL_OBJECT_KVLIST; - o->variant = cfl_variant_create_from_kvlist(ptr); + variant = cfl_variant_create_from_kvlist(ptr); + variant_created = CFL_TRUE; } else if (type == CFL_OBJECT_VARIANT) { - o->type = CFL_OBJECT_VARIANT; - o->variant = ptr; + variant = ptr; } else if (type == CFL_OBJECT_ARRAY) { - o->type = CFL_OBJECT_ARRAY; - o->variant = cfl_variant_create_from_array(ptr); + variant = cfl_variant_create_from_array(ptr); + variant_created = CFL_TRUE; } else { return -1; } + if (variant == NULL) { + return -1; + } + + if (cfl_container_adopt_variant(variant) != 0) { + if (variant_created) { + cfl_variant_destroy(variant); + } + + return -1; + } + + if (o->variant != NULL && o->variant != variant) { + cfl_variant_destroy(o->variant); + } + + o->type = type; + o->variant = variant; + return 0; } int cfl_object_print(FILE *stream, struct cfl_object *o) { - if (!o) { + int ret; + + if (stream == NULL || o == NULL) { return -1; } @@ -82,8 +140,14 @@ int cfl_object_print(FILE *stream, struct cfl_object *o) return -1; } - cfl_variant_print(stream, o->variant); - printf("\n"); + ret = cfl_variant_print(stream, o->variant); + if (ret < 0) { + return -1; + } + + if (fputc('\n', stream) == EOF) { + return -1; + } return 0; } @@ -103,4 +167,4 @@ void cfl_object_destroy(struct cfl_object *o) } free(o); -} \ No newline at end of file +} diff --git a/lib/cfl/src/cfl_sds.c b/lib/cfl/src/cfl_sds.c index ca0c01a9739..dea6d61d712 100644 --- a/lib/cfl/src/cfl_sds.c +++ b/lib/cfl/src/cfl_sds.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include @@ -34,6 +36,10 @@ size_t cfl_sds_avail(cfl_sds_t s) { struct cfl_sds *h; + if (s == NULL) { + return 0; + } + h = CFL_SDS_HEADER(s); return (size_t) (h->alloc - h->len); } @@ -44,6 +50,10 @@ static cfl_sds_t sds_alloc(size_t size) cfl_sds_t s; struct cfl_sds *head; + if (size > SIZE_MAX - CFL_SDS_HEADER_SIZE - 1) { + return NULL; + } + buf = malloc(CFL_SDS_HEADER_SIZE + size + 1); if (!buf) { return NULL; @@ -61,6 +71,10 @@ static cfl_sds_t sds_alloc(size_t size) size_t cfl_sds_alloc(cfl_sds_t s) { + if (s == NULL) { + return 0; + } + return (size_t) CFL_SDS_HEADER(s)->alloc; } @@ -71,9 +85,31 @@ cfl_sds_t cfl_sds_increase(cfl_sds_t s, size_t len) cfl_sds_t out; void *tmp; + if (s == NULL) { + return NULL; + } + out = s; - new_size = (CFL_SDS_HEADER_SIZE + cfl_sds_alloc(s) + len + 1); head = CFL_SDS_HEADER(s); + + if (len == 0) { + return s; + } + + if (head->alloc > UINT64_MAX - len) { + return NULL; + } + + if (cfl_sds_alloc(s) > SIZE_MAX - len) { + return NULL; + } + + new_size = cfl_sds_alloc(s) + len; + if (new_size > SIZE_MAX - CFL_SDS_HEADER_SIZE - 1) { + return NULL; + } + new_size += CFL_SDS_HEADER_SIZE + 1; + tmp = realloc(head, new_size); if (!tmp) { return NULL; @@ -87,6 +123,10 @@ cfl_sds_t cfl_sds_increase(cfl_sds_t s, size_t len) size_t cfl_sds_len(cfl_sds_t s) { + if (s == NULL) { + return 0; + } + return (size_t) CFL_SDS_HEADER(s)->len; } @@ -95,6 +135,10 @@ cfl_sds_t cfl_sds_create_len(const char *str, int len) cfl_sds_t s; struct cfl_sds *head; + if (len < 0) { + return NULL; + } + s = sds_alloc(len); if (!s) { return NULL; @@ -119,9 +163,12 @@ cfl_sds_t cfl_sds_create(const char *str) } else { len = strlen(str); + if (len > INT_MAX) { + return NULL; + } } - return cfl_sds_create_len(str, len); + return cfl_sds_create_len(str, (int) len); } void cfl_sds_destroy(cfl_sds_t s) @@ -139,21 +186,72 @@ void cfl_sds_destroy(cfl_sds_t s) cfl_sds_t cfl_sds_cat(cfl_sds_t s, const char *str, int len) { size_t avail; + size_t append_len; + size_t source_offset; + uintptr_t buffer_addr; + uintptr_t source_addr; struct cfl_sds *head; cfl_sds_t tmp = NULL; + const char *source; + int source_in_buffer; + + if (s == NULL || str == NULL || len < 0) { + return NULL; + } + + if (len == 0) { + return s; + } + + append_len = (size_t) len; + head = CFL_SDS_HEADER(s); + if (head->len > head->alloc || head->len > SIZE_MAX - append_len - 1) { + return NULL; + } + + source = str; + source_in_buffer = 0; + source_offset = 0; + + /* + * This flat-address check lets self-appends survive realloc. If the + * source starts inside the SDS buffer, the whole source slice must also + * fit in that allocation. + */ + buffer_addr = (uintptr_t) s; + source_addr = (uintptr_t) str; + if (source_addr >= buffer_addr && + (source_addr - buffer_addr) <= head->alloc) { + source_offset = (size_t) (source_addr - buffer_addr); + + if (append_len - 1 >= head->alloc - source_offset) { + return NULL; + } + + source_in_buffer = 1; + } avail = cfl_sds_avail(s); - if (avail < len) { - tmp = cfl_sds_increase(s, len); + if (avail < append_len) { + tmp = cfl_sds_increase(s, append_len - avail); if (!tmp) { return NULL; } s = tmp; } - memcpy((char *) (s + cfl_sds_len(s)), str, len); + + if (source_in_buffer) { + source = s + source_offset; + } head = CFL_SDS_HEADER(s); - head->len += len; + if (head->len > UINT64_MAX - append_len) { + return NULL; + } + + memmove((char *) (s + head->len), source, append_len); + + head->len += append_len; s[head->len] = '\0'; return s; @@ -168,14 +266,27 @@ void cfl_sds_set_len(cfl_sds_t s, size_t len) { struct cfl_sds *head; + if (s == NULL) { + return; + } + head = CFL_SDS_HEADER(s); + if (len > head->alloc) { + return; + } + head->len = len; + s[len] = '\0'; } void cfl_sds_cat_safe(cfl_sds_t *buf, const char *str, int len) { cfl_sds_t tmp; + if (buf == NULL || *buf == NULL) { + return; + } + tmp = cfl_sds_cat(*buf, str, len); if (!tmp) { return; @@ -186,51 +297,54 @@ void cfl_sds_cat_safe(cfl_sds_t *buf, const char *str, int len) cfl_sds_t cfl_sds_printf(cfl_sds_t *sds, const char *fmt, ...) { va_list ap; - int len = strlen(fmt)*2; + size_t avail; + size_t growth; + size_t base_len; int size; cfl_sds_t tmp = NULL; cfl_sds_t s; struct cfl_sds *head; - if (len < 64) len = 64; + if (sds == NULL || *sds == NULL || fmt == NULL) { + return NULL; + } s = *sds; - if (cfl_sds_avail(s)< len) { - tmp = cfl_sds_increase(s, len); - if (!tmp) { - return NULL; - } - *sds = s = tmp; + base_len = cfl_sds_len(s); + if (base_len > cfl_sds_alloc(s)) { + return NULL; } - va_start(ap, fmt); - size = vsnprintf((char *) (s + cfl_sds_len(s)), cfl_sds_avail(s), fmt, ap); - if (size < 0) { + while (1) { + avail = cfl_sds_avail(s); + va_start(ap, fmt); + size = vsnprintf((char *) (s + base_len), avail + 1, fmt, ap); va_end(ap); - return NULL; - } - va_end(ap); - if (size >= cfl_sds_avail(s)) { - tmp = cfl_sds_increase(s, size - cfl_sds_avail(s) + 1); - if (!tmp) { + if (size < 0) { return NULL; } - *sds = s = tmp; - va_start(ap, fmt); - size = vsnprintf((char *) (s + cfl_sds_len(s)), cfl_sds_avail(s), fmt, ap); - if (size > cfl_sds_avail(s)) { - va_end(ap); + if ((size_t) size <= avail) { + break; + } + + growth = (size_t) size - avail; + tmp = cfl_sds_increase(s, growth); + if (!tmp) { return NULL; } - va_end(ap); + + *sds = s = tmp; } head = CFL_SDS_HEADER(s); - head->len += size; + if (head->len > UINT64_MAX - (size_t) size) { + return NULL; + } + + head->len += (size_t) size; s[head->len] = '\0'; return s; } - diff --git a/lib/cfl/src/cfl_utils.c b/lib/cfl/src/cfl_utils.c index 5342134e7e5..ffb7aa36a4f 100644 --- a/lib/cfl/src/cfl_utils.c +++ b/lib/cfl/src/cfl_utils.c @@ -19,15 +19,28 @@ #include +#include +#include + /* Lookup char into string, return position * Based on monkey/monkey's mk_string_char_search. */ static int cfl_string_char_search(const char *string, int c, int len) { char *p; + size_t string_len; + + if (string == NULL) { + return -1; + } if (len < 0) { - len = strlen(string); + string_len = strlen(string); + if (string_len > INT_MAX) { + return -1; + } + + len = (int) string_len; } p = memchr(string, c, len); @@ -43,14 +56,20 @@ static int cfl_string_char_search(const char *string, int c, int len) */ static char *cfl_string_copy_substr(const char *string, int pos_init, int pos_end) { - unsigned int size, bytes; + size_t size; + size_t bytes; char *buffer = 0; - if (pos_init > pos_end) { + if (string == NULL || pos_init < 0 || pos_end < 0 || pos_init > pos_end) { return NULL; } - size = (unsigned int) (pos_end - pos_init) + 1; + bytes = (size_t) (pos_end - pos_init); + if (bytes > SIZE_MAX - 1) { + return NULL; + } + + size = bytes + 1; if (size <= 2) { size = 4; } @@ -61,7 +80,6 @@ static char *cfl_string_copy_substr(const char *string, int pos_init, int pos_en return NULL; } - bytes = pos_end - pos_init; memcpy(buffer, string + pos_init, bytes); buffer[bytes] = '\0'; @@ -74,7 +92,13 @@ static char *cfl_string_copy_substr(const char *string, int pos_init, int pos_en static int quoted_string_len(const char *str) { int len = 0; - char quote = *str++; /* Consume the quote character. */ + char quote; + + if (str == NULL) { + return -1; + } + + quote = *str++; /* Consume the quote character. */ while (quote != 0) { char c = *str++; @@ -98,6 +122,9 @@ static int quoted_string_len(const char *str) default: break; } + if (len == INT_MAX) { + return -1; + } len++; } @@ -117,11 +144,16 @@ static int quoted_string_len(const char *str) static int next_token(const char *str, int separator, char **out, int *out_len, int parse_quotes) { const char *token_in = str; char *token_out; + size_t token_len; int next_separator = 0; int quote = 0; /* Parser state: 0 not inside quoted string, or '"' or '\'' when inside quoted string. */ int len = 0; int i; + if (str == NULL || out == NULL || out_len == NULL) { + return -1; + } + /* Skip leading separators. */ while (*token_in == separator) { token_in++; @@ -129,7 +161,12 @@ static int next_token(const char *str, int separator, char **out, int *out_len, /* Should quotes be parsed? Or is token quoted? If not, copy until separator or the end of string. */ if (parse_quotes == CFL_FALSE || (*token_in != '"' && *token_in != '\'')) { - len = (int)strlen(token_in); + token_len = strlen(token_in); + if (token_len > INT_MAX) { + return -1; + } + + len = (int) token_len; next_separator = cfl_string_char_search(token_in, separator, len); if (next_separator > 0) { len = next_separator; @@ -186,6 +223,7 @@ static struct cfl_list *split(const char *line, int separator, int max_split, in int val_len; int len; int end; + size_t line_len; char *val; struct cfl_list *list; struct cfl_split_entry *new; @@ -201,7 +239,13 @@ static struct cfl_list *split(const char *line, int separator, int max_split, in } cfl_list_init(list); - len = strlen(line); + line_len = strlen(line); + if (line_len > INT_MAX) { + free(list); + return NULL; + } + + len = (int) line_len; while (i < len) { end = next_token(line + i, separator, &val, &val_len, quoted); if (end == -1) { @@ -236,13 +280,19 @@ static struct cfl_list *split(const char *line, int separator, int max_split, in * and last entry. */ if (count >= max_split && max_split > 0 && i < len) { - new = calloc(1, sizeof(struct cfl_split_entry)); + new = calloc(1, sizeof(struct cfl_split_entry)); if (!new) { cfl_errno(); cfl_utils_split_free(list); return NULL; } new->value = cfl_string_copy_substr(line, i, len); + if (new->value == NULL) { + cfl_errno(); + free(new); + cfl_utils_split_free(list); + return NULL; + } new->len = len - i; cfl_list_add(&new->_head, list); break; @@ -265,6 +315,10 @@ struct cfl_list *cfl_utils_split(const char *line, int separator, int max_split) void cfl_utils_split_free_entry(struct cfl_split_entry *entry) { + if (entry == NULL) { + return; + } + cfl_list_del(&entry->_head); free(entry->value); free(entry); @@ -276,6 +330,10 @@ void cfl_utils_split_free(struct cfl_list *list) struct cfl_list *head; struct cfl_split_entry *entry; + if (list == NULL) { + return; + } + cfl_list_foreach_safe(head, tmp, list) { entry = cfl_list_entry(head, struct cfl_split_entry, _head); cfl_utils_split_free_entry(entry); diff --git a/lib/cfl/src/cfl_variant.c b/lib/cfl/src/cfl_variant.c index 3e7065e3737..4143aa63634 100644 --- a/lib/cfl/src/cfl_variant.c +++ b/lib/cfl/src/cfl_variant.c @@ -21,13 +21,98 @@ #include #include #include +#include #include -#if defined(__MINGW32__) || defined(__MINGW64__) -#define HEXDUMPFORMAT "%#x" +#include +#include +#if defined(_MSC_VER) +#include +#endif + +static int double_is_finite(double value) +{ +#if defined(_MSC_VER) + return _finite(value); #else -#define HEXDUMPFORMAT "%p" + return isfinite(value); #endif +} + +static int print_json_string(FILE *fp, const char *str, size_t len) +{ + size_t i; + size_t written; + unsigned char c; + int ret; + + if (fputc('"', fp) == EOF) { + return -1; + } + written = 1; + + if (str != NULL) { + for (i = 0; i < len; i++) { + c = (unsigned char) str[i]; + + switch (c) { + case '"': + ret = fputs("\\\"", fp); + written += 2; + break; + case '\\': + ret = fputs("\\\\", fp); + written += 2; + break; + case '\b': + ret = fputs("\\b", fp); + written += 2; + break; + case '\f': + ret = fputs("\\f", fp); + written += 2; + break; + case '\n': + ret = fputs("\\n", fp); + written += 2; + break; + case '\r': + ret = fputs("\\r", fp); + written += 2; + break; + case '\t': + ret = fputs("\\t", fp); + written += 2; + break; + default: + if (c < 0x20) { + ret = fprintf(fp, "\\u%04x", c); + written += 6; + } + else { + ret = fputc(c, fp); + written++; + } + break; + } + + if (ret < 0) { + return -1; + } + } + } + + if (fputc('"', fp) == EOF) { + return -1; + } + written++; + + if (written > INT_MAX) { + return INT_MAX; + } + + return (int) written; +} int cfl_variant_print(FILE *fp, struct cfl_variant *val) { @@ -41,7 +126,10 @@ int cfl_variant_print(FILE *fp, struct cfl_variant *val) switch (val->type) { case CFL_VARIANT_STRING: - ret = fprintf(fp, "\"%s\"", val->data.as_string); + if (val->data.as_string == NULL && val->size > 0) { + return -1; + } + ret = print_json_string(fp, val->data.as_string, val->size); break; case CFL_VARIANT_BOOL: if (val->data.as_bool) { @@ -58,20 +146,33 @@ int cfl_variant_print(FILE *fp, struct cfl_variant *val) ret = fprintf(fp, "%" PRIu64, val->data.as_uint64); break; case CFL_VARIANT_DOUBLE: - ret = fprintf(fp, "%lf", val->data.as_double); + if (!double_is_finite(val->data.as_double)) { + ret = fputs("null", fp); + } + else { + ret = fprintf(fp, "%lf", val->data.as_double); + } break; case CFL_VARIANT_NULL: ret = fprintf(fp, "null"); break; case CFL_VARIANT_BYTES: - size = cfl_sds_len(val->data.as_bytes); - for (i=0; idata.as_bytes == NULL && val->size > 0) { + return -1; + } + + size = val->size; + ret = 0; + for (i = 0; i < size; i++) { ret = fprintf(fp, "%02x", (unsigned char)val->data.as_bytes[i]); + if (ret < 0) { + return -1; + } } break; case CFL_VARIANT_REFERENCE: - ret = fprintf(fp, HEXDUMPFORMAT, val->data.as_reference); + ret = fputs("null", fp); break; case CFL_VARIANT_ARRAY: ret = cfl_array_print(fp, val->data.as_array); @@ -91,17 +192,26 @@ struct cfl_variant *cfl_variant_create_from_string_s(char *value, size_t value_s { struct cfl_variant *instance; + if (value == NULL && value_size > 0) { + return NULL; + } + instance = cfl_variant_create(); if (!instance) { return NULL; } - instance->referenced = referenced; + instance->referenced = referenced ? CFL_TRUE : CFL_FALSE; if (referenced) { instance->data.as_string = value; } else { - instance->data.as_string = cfl_sds_create_len(value, value_size); + if (value_size > INT_MAX) { + free(instance); + return NULL; + } + + instance->data.as_string = cfl_sds_create_len(value, (int) value_size); if (instance->data.as_string == NULL) { free(instance); return NULL; @@ -116,6 +226,10 @@ struct cfl_variant *cfl_variant_create_from_string_s(char *value, size_t value_s struct cfl_variant *cfl_variant_create_from_string(char *value) { + if (value == NULL) { + return NULL; + } + return cfl_variant_create_from_string_s(value, strlen(value), CFL_FALSE); } @@ -123,17 +237,26 @@ struct cfl_variant *cfl_variant_create_from_bytes(char *value, size_t length, in { struct cfl_variant *instance; + if (value == NULL && length > 0) { + return NULL; + } + instance = cfl_variant_create(); if (!instance){ return NULL; } - instance->referenced = referenced; + instance->referenced = referenced ? CFL_TRUE : CFL_FALSE; if (referenced) { instance->data.as_bytes = value; } else { - instance->data.as_bytes = cfl_sds_create_len(value, length); + if (length > INT_MAX) { + free(instance); + return NULL; + } + + instance->data.as_bytes = cfl_sds_create_len(value, (int) length); if (instance->data.as_bytes == NULL) { free(instance); return NULL; @@ -215,6 +338,12 @@ struct cfl_variant *cfl_variant_create_from_array(struct cfl_array *value) instance = cfl_variant_create(); if (instance != NULL) { + if (value != NULL && + cfl_container_claim_array(value, instance) != 0) { + free(instance); + return NULL; + } + instance->data.as_array = value; instance->type = CFL_VARIANT_ARRAY; } @@ -228,6 +357,12 @@ struct cfl_variant *cfl_variant_create_from_kvlist(struct cfl_kvlist *value) instance = cfl_variant_create(); if (instance != NULL) { + if (value != NULL && + cfl_container_claim_kvlist(value, instance) != 0) { + free(instance); + return NULL; + } + instance->data.as_kvlist = value; instance->type = CFL_VARIANT_KVLIST; } @@ -268,6 +403,8 @@ void cfl_variant_destroy(struct cfl_variant *instance) return; } + cfl_container_release_variant(instance); + if (instance->type == CFL_VARIANT_STRING || instance->type == CFL_VARIANT_BYTES) { if (instance->data.as_string != NULL && !instance->referenced) { @@ -286,10 +423,18 @@ void cfl_variant_destroy(struct cfl_variant *instance) void cfl_variant_size_set(struct cfl_variant *var, size_t size) { + if (var == NULL) { + return; + } + var->size = size; } size_t cfl_variant_size_get(struct cfl_variant *var) { + if (var == NULL) { + return 0; + } + return var->size; } diff --git a/lib/cfl/tests/CMakeLists.txt b/lib/cfl/tests/CMakeLists.txt index 4dba27058fb..6bd3cc6f7bb 100644 --- a/lib/cfl/tests/CMakeLists.txt +++ b/lib/cfl/tests/CMakeLists.txt @@ -1,6 +1,9 @@ include_directories(lib/acutest) set(UNIT_TESTS_FILES + atomic_operations.c + checksum.c + headers.c kv.c kvlist.c array.c @@ -13,6 +16,32 @@ set(UNIT_TESTS_FILES utils.c ) +if(NOT CFL_SYSTEM_WINDOWS) + find_package(Threads REQUIRED) +endif() + +set(PUBLIC_HEADERS + cfl.h + cfl_array.h + cfl_atomic.h + cfl_checksum.h + cfl_compat.h + cfl_container.h + cfl_found.h + cfl_hash.h + cfl_info.h + cfl_kv.h + cfl_kvlist.h + cfl_list.h + cfl_log.h + cfl_object.h + cfl_sds.h + cfl_time.h + cfl_utils.h + cfl_variant.h + cfl_version.h + ) + configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/cfl_tests_internal.h.in" "${CMAKE_CURRENT_SOURCE_DIR}/cfl_tests_internal.h" @@ -28,9 +57,33 @@ foreach(source_file ${UNIT_TESTS_FILES}) ) target_link_libraries(${source_file_we} cfl-static) + if(source_file STREQUAL "atomic_operations.c" AND NOT CFL_SYSTEM_WINDOWS) + target_link_libraries(${source_file_we} Threads::Threads) + endif() + if (CFL_SANITIZE_ADDRESS) add_sanitizers(${source_file_we}) endif() add_test(${source_file_we} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${source_file_we}) endforeach() + +foreach(public_header ${PUBLIC_HEADERS}) + string(REPLACE "." "_" public_header_target ${public_header}) + set(public_header_source "${CMAKE_CURRENT_BINARY_DIR}/header_${public_header_target}.c") + file(WRITE "${public_header_source}" "#include \nint main(void) { return 0; }\n") + + add_executable( + cfl-test-header-${public_header_target} + ${public_header_source} + ) + + target_link_libraries(cfl-test-header-${public_header_target} cfl-static) + + if (CFL_SANITIZE_ADDRESS) + add_sanitizers(cfl-test-header-${public_header_target}) + endif() + + add_test(cfl-test-header-${public_header_target} + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/cfl-test-header-${public_header_target}) +endforeach() diff --git a/lib/cfl/tests/array.c b/lib/cfl/tests/array.c index 90332bbfa22..27096013e55 100644 --- a/lib/cfl/tests/array.c +++ b/lib/cfl/tests/array.c @@ -317,6 +317,140 @@ static void append_kvlist() cfl_array_destroy(arr); } +static void append_array_rejects_cycles() +{ + int ret; + struct cfl_array *arr; + struct cfl_array *child; + + arr = cfl_array_create(2); + TEST_CHECK(arr != NULL); + + ret = cfl_array_append_array(arr, arr); + TEST_CHECK(ret == -1); + + child = cfl_array_create(1); + TEST_CHECK(child != NULL); + + ret = cfl_array_append_array(arr, child); + TEST_CHECK(ret == 0); + + ret = cfl_array_append_array(arr, child); + TEST_CHECK(ret < 0); + + ret = cfl_array_append_array(child, arr); + TEST_CHECK(ret < 0); + + cfl_array_destroy(arr); +} + +static void append_variant_rejects_cycles() +{ + int ret; + struct cfl_array *arr; + struct cfl_variant *variant; + + arr = cfl_array_create(1); + TEST_CHECK(arr != NULL); + + variant = cfl_variant_create_from_array(arr); + TEST_CHECK(variant != NULL); + + ret = cfl_array_append(arr, variant); + TEST_CHECK(ret == -1); + + cfl_variant_destroy(variant); +} + +static void append_rejects_shared_array_between_parents() +{ + int ret; + struct cfl_array *arr_a; + struct cfl_array *arr_b; + struct cfl_array *child; + + arr_a = cfl_array_create(1); + TEST_CHECK(arr_a != NULL); + + arr_b = cfl_array_create(1); + TEST_CHECK(arr_b != NULL); + + child = cfl_array_create(0); + TEST_CHECK(child != NULL); + + ret = cfl_array_append_array(arr_a, child); + TEST_CHECK(ret == 0); + + ret = cfl_array_append_array(arr_b, child); + TEST_CHECK(ret == -1); + TEST_CHECK(cfl_array_size(arr_b) == 0); + + cfl_array_destroy(arr_b); + cfl_array_destroy(arr_a); +} + +static void append_rejects_owned_value() +{ + int ret; + struct cfl_array *arr_a; + struct cfl_array *arr_b; + struct cfl_variant *value; + + arr_a = cfl_array_create(1); + if (!TEST_CHECK(arr_a != NULL)) { + return; + } + + arr_b = cfl_array_create(1); + if (!TEST_CHECK(arr_b != NULL)) { + cfl_array_destroy(arr_a); + return; + } + + value = cfl_variant_create_from_string("value"); + if (!TEST_CHECK(value != NULL)) { + cfl_array_destroy(arr_b); + cfl_array_destroy(arr_a); + return; + } + + ret = cfl_array_append(arr_a, value); + if (!TEST_CHECK(ret == 0)) { + cfl_variant_destroy(value); + cfl_array_destroy(arr_b); + cfl_array_destroy(arr_a); + return; + } + + ret = cfl_array_append(arr_b, value); + TEST_CHECK(ret == -1); + TEST_CHECK(cfl_array_size(arr_b) == 0); + + cfl_array_destroy(arr_b); + cfl_array_destroy(arr_a); +} + +static void append_kvlist_rejects_cycles() +{ + int ret; + struct cfl_array *arr; + struct cfl_kvlist *kvlist; + + arr = cfl_array_create(1); + TEST_CHECK(arr != NULL); + + kvlist = cfl_kvlist_create(); + TEST_CHECK(kvlist != NULL); + + ret = cfl_array_append_kvlist(arr, kvlist); + TEST_CHECK(ret == 0); + + ret = cfl_kvlist_insert_array(kvlist, "cycle", arr); + TEST_CHECK(ret < 0); + + cfl_array_destroy(arr); +} + static void remove_by_index() { int ret; @@ -363,6 +497,70 @@ static void remove_by_reference() cfl_array_destroy(arr); } +static void print_write_error() +{ +#ifdef __linux__ + int ret; + FILE *fp; + struct cfl_array *arr; + + arr = cfl_array_create(0); + TEST_CHECK(arr != NULL); + + fp = fopen("/dev/full", "w"); + if (fp == NULL) { + cfl_array_destroy(arr); + return; + } + + setvbuf(fp, NULL, _IONBF, 0); + + ret = cfl_array_print(fp, arr); + TEST_CHECK(ret == -1); + + fclose(fp); + cfl_array_destroy(arr); +#endif +} + +static void null_inputs() +{ + int ret; + struct cfl_variant *var; + + TEST_CHECK(cfl_array_size(NULL) == 0); + + var = cfl_array_fetch_by_index(NULL, 0); + TEST_CHECK(var == NULL); + + ret = cfl_array_resizable(NULL, CFL_TRUE); + TEST_CHECK(ret == -1); + + ret = cfl_array_append(NULL, NULL); + TEST_CHECK(ret == -1); + + ret = cfl_array_append_string(NULL, "value"); + TEST_CHECK(ret < 0); + + ret = cfl_array_append_string(NULL, NULL); + TEST_CHECK(ret == -1); + + ret = cfl_array_append_bytes(NULL, NULL, 1, CFL_TRUE); + TEST_CHECK(ret == -1); + + ret = cfl_array_append_array(NULL, NULL); + TEST_CHECK(ret == -1); + + ret = cfl_array_append_kvlist(NULL, NULL); + TEST_CHECK(ret == -1); + + ret = cfl_array_remove_by_index(NULL, 0); + TEST_CHECK(ret == -1); + + ret = cfl_array_remove_by_reference(NULL, NULL); + TEST_CHECK(ret == -1); +} + TEST_LIST = { {"create", create}, {"resizable", resizable}, @@ -380,7 +578,14 @@ TEST_LIST = { {"append_array", append_array}, {"append_new_array", append_new_array}, {"append_kvlist", append_kvlist}, + {"append_array_rejects_cycles", append_array_rejects_cycles}, + {"append_variant_rejects_cycles", append_variant_rejects_cycles}, + {"append_rejects_shared_array_between_parents", append_rejects_shared_array_between_parents}, + {"append_rejects_owned_value", append_rejects_owned_value}, + {"append_kvlist_rejects_cycles", append_kvlist_rejects_cycles}, {"remove_by_index", remove_by_index}, {"remove_by_reference", remove_by_reference}, + {"print_write_error", print_write_error}, + {"null_inputs", null_inputs}, { 0 } }; diff --git a/lib/cfl/tests/atomic_operations.c b/lib/cfl/tests/atomic_operations.c new file mode 100644 index 00000000000..add799ec663 --- /dev/null +++ b/lib/cfl/tests/atomic_operations.c @@ -0,0 +1,177 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CFL + * === + * Copyright (C) 2022 The CFL Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#if defined (_WIN32) || defined (_WIN64) +#include +#else +#include +#endif + +#include "cfl_tests_internal.h" + +#define THREAD_COUNT 100 +#define CYCLE_COUNT 10000 +#define EXPECTED_VALUE (THREAD_COUNT * CYCLE_COUNT) + +static uint64_t global_counter; + +static void test_atomic_initialize() +{ + TEST_CHECK(cfl_atomic_initialize() == 0); + TEST_CHECK(cfl_atomic_initialize() == 0); + TEST_CHECK(cfl_init() == 0); +} + +static void test_atomic_basic_operations() +{ + TEST_CHECK(cfl_init() == 0); + + cfl_atomic_store(&global_counter, 10); + TEST_CHECK(cfl_atomic_load(&global_counter) == 10); + + TEST_CHECK(cfl_atomic_compare_exchange(&global_counter, 5, 20) == 0); + TEST_CHECK(cfl_atomic_load(&global_counter) == 10); + + TEST_CHECK(cfl_atomic_compare_exchange(&global_counter, 10, 20) == 1); + TEST_CHECK(cfl_atomic_load(&global_counter) == 20); +} + +static void test_atomic_full_width_values() +{ + uint64_t value; + uint64_t high_bit; + + high_bit = UINT64_C(1) << 63; + + TEST_CHECK(cfl_atomic_initialize() == 0); + + value = 0; + cfl_atomic_store(&value, UINT64_MAX); + TEST_CHECK(cfl_atomic_load(&value) == UINT64_MAX); + + TEST_CHECK(cfl_atomic_compare_exchange(&value, high_bit, 1) == 0); + TEST_CHECK(cfl_atomic_load(&value) == UINT64_MAX); + + TEST_CHECK(cfl_atomic_compare_exchange(&value, UINT64_MAX, high_bit) == 1); + TEST_CHECK(cfl_atomic_load(&value) == high_bit); + + TEST_CHECK(cfl_atomic_compare_exchange(&value, high_bit, 0) == 1); + TEST_CHECK(cfl_atomic_load(&value) == 0); +} + +static void add_through_compare_exchange(uint64_t val) +{ + uint64_t old; + uint64_t new; + int result; + + do { + old = cfl_atomic_load(&global_counter); + new = old + val; + + result = cfl_atomic_compare_exchange(&global_counter, old, new); + } + while (result == 0); +} + +#if defined (_WIN32) || defined (_WIN64) +static DWORD WINAPI worker_thread_add_through_compare_exchange(LPVOID ptr) +#else +static void *worker_thread_add_through_compare_exchange(void *ptr) +#endif +{ + int local_counter; + + (void) ptr; + + for (local_counter = 0; local_counter < CYCLE_COUNT; local_counter++) { + add_through_compare_exchange(1); + } + +#if defined (_WIN32) || defined (_WIN64) + return 0; +#else + return NULL; +#endif +} + +#if defined (_WIN32) || defined (_WIN64) + +static void test_atomic_operations() +{ + HANDLE threads[THREAD_COUNT]; + DWORD thread_ids[THREAD_COUNT]; + int thread_index; + DWORD result; + + TEST_CHECK(cfl_init() == 0); + + cfl_atomic_store(&global_counter, 0); + + for (thread_index = 0; thread_index < THREAD_COUNT; thread_index++) { + threads[thread_index] = CreateThread(NULL, 0, + worker_thread_add_through_compare_exchange, + NULL, 0, &thread_ids[thread_index]); + } + + for (thread_index = 0; thread_index < THREAD_COUNT; thread_index++) { + result = WaitForSingleObject(threads[thread_index], INFINITE); + TEST_CHECK(result == WAIT_OBJECT_0); + CloseHandle(threads[thread_index]); + } + + TEST_CHECK(cfl_atomic_load(&global_counter) == EXPECTED_VALUE); +} + +#else + +static void test_atomic_operations() +{ + pthread_t threads[THREAD_COUNT]; + int thread_index; + int result; + + TEST_CHECK(cfl_init() == 0); + + cfl_atomic_store(&global_counter, 0); + + for (thread_index = 0; thread_index < THREAD_COUNT; thread_index++) { + result = pthread_create(&threads[thread_index], NULL, + worker_thread_add_through_compare_exchange, NULL); + TEST_CHECK(result == 0); + } + + for (thread_index = 0; thread_index < THREAD_COUNT; thread_index++) { + result = pthread_join(threads[thread_index], NULL); + TEST_CHECK(result == 0); + } + + TEST_CHECK(cfl_atomic_load(&global_counter) == EXPECTED_VALUE); +} +#endif + +TEST_LIST = { + { "atomic_initialize", test_atomic_initialize }, + { "atomic_basic_operations", test_atomic_basic_operations }, + { "atomic_full_width_values", test_atomic_full_width_values }, + { "atomic_operations", test_atomic_operations }, + { 0 } +}; diff --git a/lib/cfl/tests/checksum.c b/lib/cfl/tests/checksum.c new file mode 100644 index 00000000000..32dc3e5d04d --- /dev/null +++ b/lib/cfl/tests/checksum.c @@ -0,0 +1,38 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CFL + * === + * Copyright (C) 2022 The CFL Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "cfl_tests_internal.h" + +static void crc32c_null_input() +{ + uint32_t crc; + + crc = cfl_checksum_crc32c(NULL, 0); + TEST_CHECK(crc == 0); + + crc = cfl_checksum_crc32c(NULL, 1); + TEST_CHECK(crc == 0); +} + +TEST_LIST = { + {"crc32c_null_input", crc32c_null_input}, + { 0 } +}; diff --git a/lib/cfl/tests/headers.c b/lib/cfl/tests/headers.c new file mode 100644 index 00000000000..5b5f890ea91 --- /dev/null +++ b/lib/cfl/tests/headers.c @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* CFL + * === + * Copyright (C) 2022 The CFL Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cfl_tests_internal.h" + +static void public_headers_compile() +{ + TEST_CHECK(cfl_found() == 0); +} + +TEST_LIST = { + {"public_headers_compile", public_headers_compile}, + { 0 } +}; diff --git a/lib/cfl/tests/kv.c b/lib/cfl/tests/kv.c index f49ade9d63c..531925b38dc 100644 --- a/lib/cfl/tests/kv.c +++ b/lib/cfl/tests/kv.c @@ -76,7 +76,30 @@ static void regular_operation() cfl_kv_release(&entry_list); } +static void null_inputs() +{ + struct cfl_kv *entry; + + cfl_kv_init(NULL); + + entry = cfl_kv_item_create(NULL, "key", "value"); + TEST_CHECK(entry == NULL); + + entry = cfl_kv_item_create_len(NULL, "key", 3, "value", 5); + TEST_CHECK(entry == NULL); + + entry = cfl_kv_item_create_len(NULL, "key", 3, NULL, 1); + TEST_CHECK(entry == NULL); + + TEST_CHECK(cfl_kv_get_key_value(NULL, NULL) == NULL); + TEST_CHECK(cfl_kv_get_key_value("key", NULL) == NULL); + + cfl_kv_item_destroy(NULL); + cfl_kv_release(NULL); +} + TEST_LIST = { {"regular_operation", regular_operation}, + {"null_inputs", null_inputs}, { 0 } }; diff --git a/lib/cfl/tests/kvlist.c b/lib/cfl/tests/kvlist.c index 87a55932361..ac073750a7d 100644 --- a/lib/cfl/tests/kvlist.c +++ b/lib/cfl/tests/kvlist.c @@ -24,6 +24,30 @@ #include "cfl_tests_internal.h" +static int compare(FILE *fp, char *expect) +{ + size_t len; + size_t ret_fp; + char buf[256] = {0}; + + len = strlen(expect); + + if (fseek(fp, 0, SEEK_SET) != 0) { + return -1; + } + + ret_fp = fread(&buf[0], 1, sizeof(buf) - 1, fp); + if (ret_fp == 0 && ferror(fp)) { + return -1; + } + + if (strlen(buf) != len) { + return -1; + } + + return strncmp(expect, &buf[0], len); +} + static void create_destroy() { struct cfl_kvlist *list = NULL; @@ -1166,6 +1190,328 @@ static void test_basics() cfl_kvlist_destroy(list); } +static void null_inputs() +{ + int ret; + struct cfl_kvlist *list; + struct cfl_variant *variant; + + cfl_kvlist_destroy(NULL); + + ret = cfl_kvlist_count(NULL); + TEST_CHECK(ret == 0); + + variant = cfl_kvlist_fetch(NULL, "key"); + TEST_CHECK(variant == NULL); + + variant = cfl_kvlist_fetch_s(NULL, "key", 3); + TEST_CHECK(variant == NULL); + + ret = cfl_kvlist_contains(NULL, "key"); + TEST_CHECK(ret == CFL_FALSE); + + ret = cfl_kvlist_remove(NULL, "key"); + TEST_CHECK(ret == CFL_FALSE); + + ret = cfl_kvlist_insert_string(NULL, "key", "value"); + TEST_CHECK(ret == -1); + + list = cfl_kvlist_create(); + TEST_CHECK(list != NULL); + + ret = cfl_kvlist_insert_string(list, NULL, "value"); + TEST_CHECK(ret == -1); + + ret = cfl_kvlist_insert_bytes(list, "key", NULL, 1, CFL_TRUE); + TEST_CHECK(ret == -1); + + ret = cfl_kvlist_insert_array(list, "key", NULL); + TEST_CHECK(ret == -1); + + ret = cfl_kvlist_insert_kvlist(list, "key", NULL); + TEST_CHECK(ret == -1); + + ret = cfl_kvlist_insert(list, "key", NULL); + TEST_CHECK(ret == -1); + + variant = cfl_kvlist_fetch(list, NULL); + TEST_CHECK(variant == NULL); + + cfl_kvpair_destroy(NULL); + + variant = cfl_kvpair_take_value(NULL); + TEST_CHECK(variant == NULL); + + cfl_kvlist_destroy(list); +} + +static void print_escaped_keys() +{ + int ret; + FILE *fp; + struct cfl_kvlist *list; + + list = cfl_kvlist_create(); + if (!TEST_CHECK(list != NULL)) { + return; + } + + ret = cfl_kvlist_insert_string(list, "a\"b\n", "v\n"); + if (!TEST_CHECK(ret == 0)) { + cfl_kvlist_destroy(list); + return; + } + + fp = tmpfile(); + if (!TEST_CHECK(fp != NULL)) { + cfl_kvlist_destroy(list); + return; + } + + ret = cfl_kvlist_print(fp, list); + if (!TEST_CHECK(ret > 0)) { + fclose(fp); + cfl_kvlist_destroy(list); + return; + } + + ret = compare(fp, "{\"a\\\"b\\n\":\"v\\n\"}"); + TEST_CHECK(ret == 0); + + fclose(fp); + cfl_kvlist_destroy(list); +} + +static void embedded_nul_keys_do_not_match_short_name() +{ + int ret; + char key[] = {'a', 'd', 'm', 'i', 'n', '\0', 'x'}; + struct cfl_kvlist *list; + struct cfl_variant *variant; + + list = cfl_kvlist_create(); + TEST_CHECK(list != NULL); + + ret = cfl_kvlist_insert_string(list, "admin", "plain"); + TEST_CHECK(ret == 0); + + ret = cfl_kvlist_insert_string_s(list, key, sizeof(key), + "hidden", 6, CFL_FALSE); + TEST_CHECK(ret == 0); + + ret = cfl_kvlist_contains(list, "admin"); + TEST_CHECK(ret == CFL_TRUE); + + ret = cfl_kvlist_remove(list, "admin"); + TEST_CHECK(ret == CFL_TRUE); + + ret = cfl_kvlist_count(list); + TEST_CHECK(ret == 1); + + variant = cfl_kvlist_fetch_s(list, key, sizeof(key)); + TEST_CHECK(variant != NULL); + + ret = cfl_kvlist_contains(list, "admin"); + TEST_CHECK(ret == CFL_FALSE); + + ret = cfl_kvlist_remove(list, "admin"); + TEST_CHECK(ret == CFL_FALSE); + + ret = cfl_kvlist_count(list); + TEST_CHECK(ret == 1); + + cfl_kvlist_destroy(list); +} + +static void print_write_error() +{ +#ifdef __linux__ + int ret; + FILE *fp; + struct cfl_kvlist *list; + + list = cfl_kvlist_create(); + TEST_CHECK(list != NULL); + + fp = fopen("/dev/full", "w"); + if (fp == NULL) { + cfl_kvlist_destroy(list); + return; + } + + setvbuf(fp, NULL, _IONBF, 0); + + ret = cfl_kvlist_print(fp, list); + TEST_CHECK(ret == -1); + + fclose(fp); + cfl_kvlist_destroy(list); +#endif +} + +static void reject_kvlist_cycles() +{ + int ret; + struct cfl_kvlist *list; + struct cfl_kvlist *child; + + list = cfl_kvlist_create(); + TEST_CHECK(list != NULL); + + ret = cfl_kvlist_insert_kvlist(list, "self", list); + TEST_CHECK(ret == -1); + + child = cfl_kvlist_create(); + TEST_CHECK(child != NULL); + + ret = cfl_kvlist_insert_kvlist(list, "child", child); + TEST_CHECK(ret == 0); + + ret = cfl_kvlist_insert_kvlist(list, "child-again", child); + TEST_CHECK(ret == -1); + + ret = cfl_kvlist_insert_kvlist(child, "parent", list); + TEST_CHECK(ret < 0); + + cfl_kvlist_destroy(list); +} + +static void reject_variant_cycles() +{ + int ret; + struct cfl_kvlist *list; + struct cfl_variant *variant; + + list = cfl_kvlist_create(); + TEST_CHECK(list != NULL); + + variant = cfl_variant_create_from_kvlist(list); + TEST_CHECK(variant != NULL); + + ret = cfl_kvlist_insert(list, "self", variant); + TEST_CHECK(ret == -1); + + cfl_variant_destroy(variant); +} + +static void reject_shared_kvlist_between_parents() +{ + int ret; + struct cfl_kvlist *list_a; + struct cfl_kvlist *list_b; + struct cfl_kvlist *child; + + list_a = cfl_kvlist_create(); + TEST_CHECK(list_a != NULL); + + list_b = cfl_kvlist_create(); + TEST_CHECK(list_b != NULL); + + child = cfl_kvlist_create(); + TEST_CHECK(child != NULL); + + ret = cfl_kvlist_insert_kvlist(list_a, "child", child); + TEST_CHECK(ret == 0); + + ret = cfl_kvlist_insert_kvlist(list_b, "child", child); + TEST_CHECK(ret == -1); + TEST_CHECK(cfl_kvlist_count(list_b) == 0); + + cfl_kvlist_destroy(list_b); + cfl_kvlist_destroy(list_a); +} + +static void reject_array_cycles() +{ + int ret; + struct cfl_array *array; + struct cfl_kvlist *list; + + list = cfl_kvlist_create(); + TEST_CHECK(list != NULL); + + array = cfl_array_create(1); + TEST_CHECK(array != NULL); + + ret = cfl_kvlist_insert_array(list, "array", array); + TEST_CHECK(ret == 0); + + ret = cfl_array_append_kvlist(array, list); + TEST_CHECK(ret < 0); + + cfl_kvlist_destroy(list); +} + +static void move_taken_value_between_kvlists() +{ + int ret; + struct cfl_list *head; + struct cfl_kvlist *source; + struct cfl_kvlist *destination; + struct cfl_kvpair *pair; + struct cfl_variant *value; + struct cfl_variant *moved; + + source = cfl_kvlist_create(); + TEST_CHECK(source != NULL); + + destination = cfl_kvlist_create(); + TEST_CHECK(destination != NULL); + + ret = cfl_kvlist_insert_string(source, "source", "value"); + TEST_CHECK(ret == 0); + + head = source->list.next; + pair = cfl_list_entry(head, struct cfl_kvpair, _head); + value = cfl_kvpair_take_value(pair); + TEST_CHECK(value != NULL); + TEST_CHECK(pair->val == NULL); + + cfl_kvpair_destroy(pair); + TEST_CHECK(cfl_kvlist_count(source) == 0); + + ret = cfl_kvlist_insert(destination, "destination", value); + TEST_CHECK(ret == 0); + + moved = cfl_kvlist_fetch(destination, "destination"); + TEST_CHECK(moved == value); + TEST_CHECK(moved->type == CFL_VARIANT_STRING); + TEST_CHECK(strcmp(moved->data.as_string, "value") == 0); + + cfl_kvlist_destroy(source); + cfl_kvlist_destroy(destination); +} + +static void insert_rejects_owned_value() +{ + int ret; + struct cfl_list *head; + struct cfl_kvlist *source; + struct cfl_kvlist *destination; + struct cfl_kvpair *pair; + struct cfl_variant *value; + + source = cfl_kvlist_create(); + TEST_CHECK(source != NULL); + + destination = cfl_kvlist_create(); + TEST_CHECK(destination != NULL); + + ret = cfl_kvlist_insert_string(source, "source", "value"); + TEST_CHECK(ret == 0); + + head = source->list.next; + pair = cfl_list_entry(head, struct cfl_kvpair, _head); + value = pair->val; + + ret = cfl_kvlist_insert(destination, "destination", value); + TEST_CHECK(ret == -1); + TEST_CHECK(cfl_kvlist_count(destination) == 0); + + cfl_kvlist_destroy(source); + cfl_kvlist_destroy(destination); +} + TEST_LIST = { {"create_destroy", create_destroy}, {"count", count}, @@ -1193,5 +1539,15 @@ TEST_LIST = { {"insert_empty_array_s", insert_empty_array_s}, {"insert_empty_kvlist_s", insert_empty_kvlist_s}, {"basics", test_basics}, + {"null_inputs", null_inputs}, + {"print_escaped_keys", print_escaped_keys}, + {"embedded_nul_keys_do_not_match_short_name", embedded_nul_keys_do_not_match_short_name}, + {"print_write_error", print_write_error}, + {"reject_kvlist_cycles", reject_kvlist_cycles}, + {"reject_variant_cycles", reject_variant_cycles}, + {"reject_shared_kvlist_between_parents", reject_shared_kvlist_between_parents}, + {"reject_array_cycles", reject_array_cycles}, + {"move_taken_value_between_kvlists", move_taken_value_between_kvlists}, + {"insert_rejects_owned_value", insert_rejects_owned_value}, { 0 } }; diff --git a/lib/cfl/tests/object.c b/lib/cfl/tests/object.c index 4c372e3e1df..872cee3091b 100644 --- a/lib/cfl/tests/object.c +++ b/lib/cfl/tests/object.c @@ -21,6 +21,30 @@ #include #include "cfl_tests_internal.h" +static int compare(FILE *fp, char *expect) +{ + size_t len; + size_t ret_fp; + char buf[128] = {0}; + + len = strlen(expect); + + if (fseek(fp, 0, SEEK_SET) != 0) { + return -1; + } + + ret_fp = fread(&buf[0], 1, sizeof(buf) - 1, fp); + if (ret_fp == 0 && ferror(fp)) { + return -1; + } + + if (strlen(buf) != len) { + return -1; + } + + return strncmp(expect, &buf[0], len); +} + static void test_basics() { int ret; @@ -52,7 +76,269 @@ static void test_basics() cfl_object_destroy(object); } +static void test_replace_and_print() +{ + int ret; + FILE *fp; + struct cfl_object *object; + struct cfl_variant *variant; + + object = cfl_object_create(); + TEST_CHECK(object != NULL); + + variant = cfl_variant_create_from_string("first"); + TEST_CHECK(variant != NULL); + + ret = cfl_object_set(object, CFL_OBJECT_VARIANT, variant); + TEST_CHECK(ret == 0); + + variant = cfl_variant_create_from_string("second"); + TEST_CHECK(variant != NULL); + + ret = cfl_object_set(object, CFL_OBJECT_VARIANT, variant); + TEST_CHECK(ret == 0); + + ret = cfl_object_set(object, CFL_OBJECT_VARIANT, NULL); + TEST_CHECK(ret == -1); + + fp = tmpfile(); + TEST_CHECK(fp != NULL); + + ret = cfl_object_print(fp, object); + TEST_CHECK(ret == 0); + + ret = compare(fp, "\"second\"\n"); + TEST_CHECK(ret == 0); + + fclose(fp); + cfl_object_destroy(object); +} + +static void test_reuse_owned_kvlist() +{ + int ret; + FILE *fp; + struct cfl_object *object; + struct cfl_kvlist *list; + + object = cfl_object_create(); + TEST_CHECK(object != NULL); + + list = cfl_kvlist_create(); + TEST_CHECK(list != NULL); + + ret = cfl_kvlist_insert_string(list, "key", "value"); + TEST_CHECK(ret == 0); + + ret = cfl_object_set(object, CFL_OBJECT_KVLIST, list); + TEST_CHECK(ret == 0); + + list = object->variant->data.as_kvlist; + + ret = cfl_object_set(object, CFL_OBJECT_KVLIST, list); + TEST_CHECK(ret == 0); + + fp = tmpfile(); + TEST_CHECK(fp != NULL); + + ret = cfl_object_print(fp, object); + TEST_CHECK(ret == 0); + + ret = compare(fp, "{\"key\":\"value\"}\n"); + TEST_CHECK(ret == 0); + + fclose(fp); + cfl_object_destroy(object); +} + +static void test_reuse_owned_array() +{ + int ret; + FILE *fp; + struct cfl_object *object; + struct cfl_array *array; + + object = cfl_object_create(); + TEST_CHECK(object != NULL); + + array = cfl_array_create(1); + TEST_CHECK(array != NULL); + + ret = cfl_array_append_string(array, "value"); + TEST_CHECK(ret == 0); + + ret = cfl_object_set(object, CFL_OBJECT_ARRAY, array); + TEST_CHECK(ret == 0); + + array = object->variant->data.as_array; + + ret = cfl_object_set(object, CFL_OBJECT_ARRAY, array); + TEST_CHECK(ret == 0); + + fp = tmpfile(); + TEST_CHECK(fp != NULL); + + ret = cfl_object_print(fp, object); + TEST_CHECK(ret == 0); + + ret = compare(fp, "[\"value\"]\n"); + TEST_CHECK(ret == 0); + + fclose(fp); + cfl_object_destroy(object); +} + +static void test_reject_nested_kvlist_reuse() +{ + int ret; + FILE *fp; + struct cfl_object *object; + struct cfl_kvlist *outer; + struct cfl_kvlist *inner; + + object = cfl_object_create(); + TEST_CHECK(object != NULL); + + outer = cfl_kvlist_create(); + TEST_CHECK(outer != NULL); + + inner = cfl_kvlist_create(); + TEST_CHECK(inner != NULL); + + ret = cfl_kvlist_insert_kvlist(outer, "child", inner); + TEST_CHECK(ret == 0); + + ret = cfl_object_set(object, CFL_OBJECT_KVLIST, outer); + TEST_CHECK(ret == 0); + + ret = cfl_object_set(object, CFL_OBJECT_KVLIST, inner); + TEST_CHECK(ret == -1); + + fp = tmpfile(); + TEST_CHECK(fp != NULL); + + ret = cfl_object_print(fp, object); + TEST_CHECK(ret == 0); + + ret = compare(fp, "{\"child\":{}}\n"); + TEST_CHECK(ret == 0); + + fclose(fp); + cfl_object_destroy(object); +} + +static void test_reject_nested_array_reuse() +{ + int ret; + FILE *fp; + struct cfl_object *object; + struct cfl_array *outer; + struct cfl_array *inner; + + object = cfl_object_create(); + TEST_CHECK(object != NULL); + + outer = cfl_array_create(1); + TEST_CHECK(outer != NULL); + + inner = cfl_array_create(0); + TEST_CHECK(inner != NULL); + + ret = cfl_array_append_array(outer, inner); + TEST_CHECK(ret == 0); + + ret = cfl_object_set(object, CFL_OBJECT_ARRAY, outer); + TEST_CHECK(ret == 0); + + ret = cfl_object_set(object, CFL_OBJECT_ARRAY, inner); + TEST_CHECK(ret == -1); + + fp = tmpfile(); + TEST_CHECK(fp != NULL); + + ret = cfl_object_print(fp, object); + TEST_CHECK(ret == 0); + + ret = compare(fp, "[[]]\n"); + TEST_CHECK(ret == 0); + + fclose(fp); + cfl_object_destroy(object); +} + +static void test_reject_nested_variant_reuse() +{ + int ret; + FILE *fp; + struct cfl_object *object; + struct cfl_kvlist *list; + struct cfl_variant *variant; + + object = cfl_object_create(); + TEST_CHECK(object != NULL); + + list = cfl_kvlist_create(); + TEST_CHECK(list != NULL); + + ret = cfl_kvlist_insert_string(list, "key", "value"); + TEST_CHECK(ret == 0); + + variant = cfl_kvlist_fetch(list, "key"); + TEST_CHECK(variant != NULL); + + ret = cfl_object_set(object, CFL_OBJECT_KVLIST, list); + TEST_CHECK(ret == 0); + + ret = cfl_object_set(object, CFL_OBJECT_VARIANT, variant); + TEST_CHECK(ret == -1); + + fp = tmpfile(); + TEST_CHECK(fp != NULL); + + ret = cfl_object_print(fp, object); + TEST_CHECK(ret == 0); + + ret = compare(fp, "{\"key\":\"value\"}\n"); + TEST_CHECK(ret == 0); + + fclose(fp); + cfl_object_destroy(object); +} + +static void test_reject_shared_kvlist_between_objects() +{ + int ret; + struct cfl_object *object_a; + struct cfl_object *object_b; + struct cfl_kvlist *list; + + object_a = cfl_object_create(); + TEST_CHECK(object_a != NULL); + + object_b = cfl_object_create(); + TEST_CHECK(object_b != NULL); + + list = cfl_kvlist_create(); + TEST_CHECK(list != NULL); + + ret = cfl_object_set(object_a, CFL_OBJECT_KVLIST, list); + TEST_CHECK(ret == 0); + + ret = cfl_object_set(object_b, CFL_OBJECT_KVLIST, list); + TEST_CHECK(ret == -1); + + cfl_object_destroy(object_b); + cfl_object_destroy(object_a); +} + TEST_LIST = { { "test_basics", test_basics }, + { "test_replace_and_print", test_replace_and_print }, + { "test_reuse_owned_kvlist", test_reuse_owned_kvlist }, + { "test_reuse_owned_array", test_reuse_owned_array }, + { "test_reject_nested_kvlist_reuse", test_reject_nested_kvlist_reuse }, + { "test_reject_nested_array_reuse", test_reject_nested_array_reuse }, + { "test_reject_nested_variant_reuse", test_reject_nested_variant_reuse }, + { "test_reject_shared_kvlist_between_objects", test_reject_shared_kvlist_between_objects }, { 0 } }; diff --git a/lib/cfl/tests/sds.c b/lib/cfl/tests/sds.c index 899725e161f..e8baf25b30b 100644 --- a/lib/cfl/tests/sds.c +++ b/lib/cfl/tests/sds.c @@ -52,8 +52,131 @@ static void test_sds_printf() cfl_sds_destroy(s); } +static void test_sds_invalid_inputs() +{ + cfl_sds_t s; + cfl_sds_t tmp; + + tmp = cfl_sds_create_len("x", -1); + TEST_CHECK(tmp == NULL); + + s = cfl_sds_create("test"); + TEST_CHECK(s != NULL); + TEST_CHECK(cfl_sds_len(s) == 4); + + tmp = cfl_sds_cat(s, "x", -1); + TEST_CHECK(tmp == NULL); + TEST_CHECK(cfl_sds_len(s) == 4); + + tmp = cfl_sds_cat(NULL, "x", 1); + TEST_CHECK(tmp == NULL); + + tmp = cfl_sds_cat(s, NULL, 1); + TEST_CHECK(tmp == NULL); + TEST_CHECK(cfl_sds_len(s) == 4); + + cfl_sds_set_len(s, 100); + TEST_CHECK(cfl_sds_len(s) == 4); + + tmp = cfl_sds_printf(NULL, "%s", "x"); + TEST_CHECK(tmp == NULL); + + tmp = cfl_sds_printf(&s, NULL); + TEST_CHECK(tmp == NULL); + TEST_CHECK(cfl_sds_len(s) == 4); + + cfl_sds_cat_safe(NULL, "x", 1); + cfl_sds_destroy(s); +} + +static void test_sds_self_append() +{ + cfl_sds_t s; + cfl_sds_t tmp; + + s = cfl_sds_create("abcdef"); + TEST_CHECK(s != NULL); + + tmp = cfl_sds_cat(s, s, cfl_sds_len(s)); + TEST_CHECK(tmp != NULL); + s = tmp; + + TEST_CHECK(cfl_sds_len(s) == 12); + TEST_CHECK(strcmp("abcdefabcdef", s) == 0); + + cfl_sds_destroy(s); +} + +static void test_sds_rejects_oversized_in_buffer_slice() +{ + cfl_sds_t s; + cfl_sds_t tmp; + + s = cfl_sds_create("abcdef"); + TEST_CHECK(s != NULL); + + tmp = cfl_sds_cat(s, s + 4, 4); + TEST_CHECK(tmp == NULL); + TEST_CHECK(cfl_sds_len(s) == 6); + TEST_CHECK(strcmp("abcdef", s) == 0); + + cfl_sds_destroy(s); +} + +static void test_sds_in_buffer_slice_boundaries() +{ + cfl_sds_t s; + cfl_sds_t tmp; + + s = cfl_sds_create("abcdef"); + TEST_CHECK(s != NULL); + if (s == NULL) { + return; + } + + tmp = cfl_sds_cat(s, s + 4, 2); + TEST_CHECK(tmp != NULL); + if (tmp == NULL) { + cfl_sds_destroy(s); + return; + } + s = tmp; + + TEST_CHECK(cfl_sds_len(s) == 8); + TEST_CHECK(strcmp("abcdefef", s) == 0); + cfl_sds_destroy(s); + + s = cfl_sds_create("abcdef"); + TEST_CHECK(s != NULL); + if (s == NULL) { + return; + } + + tmp = cfl_sds_cat(s, s + 5, 2); + TEST_CHECK(tmp == NULL); + TEST_CHECK(cfl_sds_len(s) == 6); + TEST_CHECK(strcmp("abcdef", s) == 0); + cfl_sds_destroy(s); + + s = cfl_sds_create("abcdef"); + TEST_CHECK(s != NULL); + if (s == NULL) { + return; + } + + tmp = cfl_sds_cat(s, s + cfl_sds_alloc(s), 1); + TEST_CHECK(tmp == NULL); + TEST_CHECK(cfl_sds_len(s) == 6); + TEST_CHECK(strcmp("abcdef", s) == 0); + cfl_sds_destroy(s); +} + TEST_LIST = { { "sds_usage" , test_sds_usage}, { "sds_printf", test_sds_printf}, + { "sds_invalid_inputs", test_sds_invalid_inputs}, + { "sds_self_append", test_sds_self_append}, + { "sds_rejects_oversized_in_buffer_slice", test_sds_rejects_oversized_in_buffer_slice}, + { "sds_in_buffer_slice_boundaries", test_sds_in_buffer_slice_boundaries}, { 0 } }; diff --git a/lib/cfl/tests/utils.c b/lib/cfl/tests/utils.c index a24e0df4b7a..a3869153cf8 100644 --- a/lib/cfl/tests/utils.c +++ b/lib/cfl/tests/utils.c @@ -153,10 +153,25 @@ void test_cfl_utils_split_quoted_errors() TEST_CHECK(split == NULL); } +void test_cfl_utils_null_inputs() +{ + struct cfl_list *split = NULL; + + split = cfl_utils_split(NULL, ',', 1); + TEST_CHECK(split == NULL); + + split = cfl_utils_split_quoted(NULL, ',', 1); + TEST_CHECK(split == NULL); + + cfl_utils_split_free_entry(NULL); + cfl_utils_split_free(NULL); +} + TEST_LIST = { { "test_flb_utils_split", test_cfl_utils_split }, { "test_flb_utils_split_quoted", test_cfl_utils_split_quoted}, { "test_flb_utils_split_quoted_errors", test_cfl_utils_split_quoted_errors}, + { "test_cfl_utils_null_inputs", test_cfl_utils_null_inputs}, { 0 } }; diff --git a/lib/cfl/tests/variant.c b/lib/cfl/tests/variant.c index 1824001cb57..ecc79dcf5f7 100644 --- a/lib/cfl/tests/variant.c +++ b/lib/cfl/tests/variant.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -383,6 +384,48 @@ static void test_variant_print_double() } } +static void test_variant_print_nonfinite_double() +{ + int ret; + int i; + double inputs[] = {HUGE_VAL, -HUGE_VAL, HUGE_VAL - HUGE_VAL}; + char *expect = "null"; + + FILE *fp = NULL; + struct cfl_variant *val = NULL; + + for (i=0; i 0); + + ret = compare(fp, expect, 0); + TEST_CHECK(ret == 0); + + cfl_variant_destroy(val); + fclose(fp); +} + +static void test_variant_print_escaped_string() +{ + int ret; + char input[] = "line\n\"quoted\"\\"; + char *expect = "\"line\\n\\\"quoted\\\"\\\\\""; + FILE *fp = NULL; + struct cfl_variant *val = NULL; + + fp = tmpfile(); + if (!TEST_CHECK(fp != NULL)) { + TEST_MSG("fp is NULL"); + return; + } + + val = cfl_variant_create_from_string(input); + if (!TEST_CHECK(val != NULL)) { + TEST_MSG("cfl_variant_create_from_string failed"); + fclose(fp); + return; + } + + ret = cfl_variant_print(fp, val); + TEST_CHECK(ret > 0); + + ret = compare(fp, expect, 0); + TEST_CHECK(ret == 0); + + cfl_variant_destroy(val); + fclose(fp); +} + static void test_variant_print_bytes() { int ret; @@ -509,12 +614,64 @@ static void test_variant_print_bytes() fclose(fp); } +static void test_variant_print_referenced_bytes() +{ + int ret; + char input[] = {0x1f, 0xaa, 0x0a, 0xff}; + char *expect = "1faa0aff"; + FILE *fp = NULL; + struct cfl_variant *val = NULL; + + fp = tmpfile(); + if (!TEST_CHECK(fp != NULL)) { + TEST_MSG("fp is NULL"); + return; + } + + val = cfl_variant_create_from_bytes(input, 4, CFL_TRUE); + if (!TEST_CHECK(val != NULL)) { + TEST_MSG("cfl_variant_create_from_bytes failed"); + fclose(fp); + return; + } + + ret = cfl_variant_print(fp, val); + TEST_CHECK(ret > 0); + + ret = compare(fp, expect, 0); + TEST_CHECK(ret == 0); + + cfl_variant_destroy(val); + fclose(fp); +} + +static void test_variant_invalid_inputs() +{ + struct cfl_variant *val; + int ret; + + val = cfl_variant_create_from_string(NULL); + TEST_CHECK(val == NULL); + + val = cfl_variant_create_from_string_s(NULL, 1, CFL_FALSE); + TEST_CHECK(val == NULL); + + val = cfl_variant_create_from_bytes(NULL, 1, CFL_TRUE); + TEST_CHECK(val == NULL); + + TEST_CHECK(cfl_variant_size_get(NULL) == 0); + cfl_variant_size_set(NULL, 1); + + ret = cfl_variant_print(NULL, NULL); + TEST_CHECK(ret == -1); +} + static void test_variant_print_reference() { int ret; int *input = (int*)0x12345678; - char expect[] = "0x12345678"; + char expect[] = "null"; FILE *fp = NULL; struct cfl_variant *val = NULL; @@ -533,7 +690,7 @@ static void test_variant_print_reference() } ret = cfl_variant_print(fp, val); - if (!TEST_CHECK(ret > 0)) { + if (!TEST_CHECK(ret != EOF)) { TEST_MSG("cfl_variant_print failed"); fclose(fp); cfl_variant_destroy(val); @@ -589,9 +746,14 @@ TEST_LIST = { {"variant_print_int64", test_variant_print_int64}, {"variant_print_uint64", test_variant_print_uint64}, {"variant_print_double", test_variant_print_double}, + {"variant_print_nonfinite_double", test_variant_print_nonfinite_double}, {"variant_print_string", test_variant_print_string}, {"variant_print_string_s", test_variant_print_string_s}, + {"variant_print_sized_string_without_nul", test_variant_print_sized_string_without_nul}, + {"variant_print_escaped_string", test_variant_print_escaped_string}, {"variant_print_bytes", test_variant_print_bytes}, + {"variant_print_referenced_bytes", test_variant_print_referenced_bytes}, + {"variant_invalid_inputs", test_variant_invalid_inputs}, {"variant_print_array", test_variant_print_array}, {"variant_print_kvlist", test_variant_print_kvlist}, {"variant_print_reference", test_variant_print_reference}, From 15dbd09bf5fee945961f35db418a577f262ef634 Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Wed, 13 May 2026 15:40:42 -0600 Subject: [PATCH 2/3] config_format: detach YAML variants before transfer Signed-off-by: Eduardo Silva --- src/config_format/flb_cf_yaml.c | 36 ++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/config_format/flb_cf_yaml.c b/src/config_format/flb_cf_yaml.c index ddd8fb088e8..0e2536694b4 100644 --- a/src/config_format/flb_cf_yaml.c +++ b/src/config_format/flb_cf_yaml.c @@ -703,6 +703,7 @@ static enum status state_move_into_config_group(struct parser_state *state, stru struct cfl_list *tmp; struct cfl_kvpair *kvp; struct cfl_variant *varr; + struct cfl_variant *value; struct cfl_array *arr; struct cfl_kvlist *copy; @@ -736,22 +737,28 @@ static enum status state_move_into_config_group(struct parser_state *state, stru copy = cfl_kvlist_create(); if (copy == NULL) { - cfl_array_destroy(arr); flb_error("unable to allocate kvlist"); return YAML_FAILURE; } cfl_list_foreach_safe(head, tmp, &state->keyvals->list) { kvp = cfl_list_entry(head, struct cfl_kvpair, _head); + value = cfl_kvpair_take_value(kvp); - if (cfl_kvlist_insert(copy, kvp->key, kvp->val) < 0) { + if (value == NULL) { + flb_error("unable to take kvpair value"); + cfl_kvlist_destroy(copy); + return YAML_FAILURE; + } + + if (cfl_kvlist_insert(copy, kvp->key, value) < 0) { flb_error("unable to insert to kvlist"); + cfl_variant_destroy(value); cfl_kvlist_destroy(copy); return YAML_FAILURE; } /* ownership moved to the config group */ - kvp->val = NULL; cfl_kvpair_destroy(kvp); } @@ -768,6 +775,7 @@ static enum status state_copy_into_properties(struct parser_state *state, struct struct cfl_list *head; struct cfl_kvpair *kvp; struct cfl_variant *var; + struct cfl_variant *value; struct cfl_array *arr; size_t idx; size_t entry_count; @@ -820,27 +828,41 @@ static enum status state_copy_into_properties(struct parser_state *state, struct } } else { + value = cfl_kvpair_take_value(kvp); + + if (value == NULL) { + flb_error("unable to take variant property"); + return YAML_FAILURE; + } + if (flb_cf_section_property_add_variant(conf, properties, kvp->key, cfl_sds_len(kvp->key), - kvp->val) == NULL) { + value) == NULL) { flb_error("unable to add variant property"); + cfl_variant_destroy(value); return YAML_FAILURE; } - kvp->val = NULL; } break; case CFL_VARIANT_KVLIST: + value = cfl_kvpair_take_value(kvp); + + if (value == NULL) { + flb_error("unable to take variant property"); + return YAML_FAILURE; + } + if (flb_cf_section_property_add_variant(conf, properties, kvp->key, cfl_sds_len(kvp->key), - kvp->val) == NULL) { + value) == NULL) { flb_error("unable to add variant property"); + cfl_variant_destroy(value); return YAML_FAILURE; } - kvp->val = NULL; break; default: flb_error("unknown value type for properties: %d", kvp->val->type); From f9315b3b741e343fec44be7d67d45cc6c1837343 Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Wed, 13 May 2026 17:37:26 -0600 Subject: [PATCH 3/3] config_format: clean up transferred YAML variants Signed-off-by: Eduardo Silva --- src/config_format/flb_cf_yaml.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/config_format/flb_cf_yaml.c b/src/config_format/flb_cf_yaml.c index 0e2536694b4..204d4d5b0ea 100644 --- a/src/config_format/flb_cf_yaml.c +++ b/src/config_format/flb_cf_yaml.c @@ -773,6 +773,7 @@ static enum status state_move_into_config_group(struct parser_state *state, stru static enum status state_copy_into_properties(struct parser_state *state, struct flb_cf *conf, struct cfl_kvlist *properties) { struct cfl_list *head; + struct cfl_list *tmp; struct cfl_kvpair *kvp; struct cfl_variant *var; struct cfl_variant *value; @@ -781,7 +782,7 @@ static enum status state_copy_into_properties(struct parser_state *state, struct size_t entry_count; int array_all_strings; - cfl_list_foreach(head, &state->keyvals->list) { + cfl_list_foreach_safe(head, tmp, &state->keyvals->list) { kvp = cfl_list_entry(head, struct cfl_kvpair, _head); switch (kvp->val->type) { case CFL_VARIANT_STRING: @@ -844,6 +845,7 @@ static enum status state_copy_into_properties(struct parser_state *state, struct cfl_variant_destroy(value); return YAML_FAILURE; } + cfl_kvpair_destroy(kvp); } break; case CFL_VARIANT_KVLIST: @@ -863,6 +865,7 @@ static enum status state_copy_into_properties(struct parser_state *state, struct cfl_variant_destroy(value); return YAML_FAILURE; } + cfl_kvpair_destroy(kvp); break; default: flb_error("unknown value type for properties: %d", kvp->val->type);