JSONK is a JSON parsing, manipulation, and atomic patching library designed specifically for Linux kernel space. It provides efficient JSON operations with memory safety and atomic patching capabilities.
- RFC 8259 Compliant: Full JSON specification support
- Atomic JSON Patching: Apply partial updates to JSON objects with rollback safety
- Path-Based Access: Access nested values using dot notation (e.g., "user.profile.name")
- Memory Safe: Built-in limits and validation to prevent DoS attacks and UAF vulnerabilities
- Reference Counting: Automatic memory management with reference counting
- Lock-Free Design: Callers handle synchronization for maximum flexibility
- Zero Dependencies: No external dependencies beyond standard kernel APIs
JSONK implements the complete JSON specification (RFC 8259):
- All JSON Data Types: null, boolean, number, string, object, array
- String Escaping: Complete support for
\",\\,\/,\b,\f,\n,\r,\t - Unicode Escapes:
\uXXXXsequences (stored literally for kernel efficiency) - Number Parsing: Integers, decimals, scientific notation, negative numbers
- Validation: Proper syntax checking, no leading zeros, control character rejection
- Whitespace Handling: Correct parsing of JSON whitespace
- Nested Structures: Objects and arrays with configurable depth limits
- Error Handling: Robust parsing with detailed error reporting
JSONK delivers excellent performance for kernel space JSON operations:
- Small JSON Parsing (~833 bytes, 10 objects): 425K ops/sec (337 MB/s throughput)
- Medium JSON Parsing (~8KB, 100 objects): 13.8K ops/sec (859 MB/s throughput)
- Large JSON Parsing (~1MB, 200 objects): 1.12K ops/sec (972 MB/s throughput)
- JSON Serialization: 5.94M ops/sec (872 MB/s throughput)
- JSON Patching: 827K ops/sec (42 MB/s throughput)
- Scalability: Excellent scaling (53-220 ns per element as size increases)
Performance measured on Linux 6.8.0 in kernel space
jsonk/
├── src/ # Core library source
│ └── jsonk.c # Main implementation
├── include/ # Header files
│ └── jsonk.h # Public API
├── examples/ # Usage examples
│ └── basic_usage.c # Comprehensive usage examples
├── tests/ # Test modules
│ ├── atomic_test.c # Atomic patching tests
│ └── performance_test.c # Comprehensive performance tests
└── Makefile # Build system
JSONK includes a complete development environment using Docker devcontainers that works seamlessly with VS Code and supports both local development (using Colima on macOS) and cloud development (GitHub Codespaces).
- VS Code with the Dev Containers extension
- Homebrew (will be installed automatically if missing)
- VS Code with the Dev Containers extension
- Docker (will be installed automatically if missing)
- Just a web browser! No local setup required.
-
Clone the repository:
git clone <repository-url> cd jsonk
-
Run the local setup script (macOS/Linux only):
bash .devcontainer/local-setup.sh
This script will:
- macOS: Install Homebrew, Colima, and Docker CLI, then start Colima with optimized settings
- Linux: Install Docker and development tools
-
Open in VS Code:
code . -
Start development container:
- VS Code will prompt you to "Reopen in Container" - click it
- Or use Command Palette (
Cmd/Ctrl+Shift+P) → "Dev Containers: Reopen in Container"
The setup script configures Colima with the following settings:
- 4 CPU cores
- 8GB RAM
- 20GB disk space
- VirtioFS mount type for better file system performance
- ARM64 architecture (Apple Silicon optimized)
If you prefer manual setup or need to troubleshoot:
-
Install Colima (macOS):
brew install colima docker colima start --cpu 4 --memory 8 --disk 20 --vm-type vz --arch aarch64 --mount-type virtiofs
-
Install Docker (Linux):
sudo apt-get update sudo apt-get install -y docker.io sudo usermod -aG docker $USER # Log out and back in
-
Open in VS Code and select "Reopen in Container"
# Check Colima status
colima status
# Restart Colima if needed
colima stop
colima start --cpu 4 --memory 8 --disk 20 --vm-type vz --arch aarch64 --mount-type virtiofs
# Check Docker connectivity
docker info# Rebuild container
# In VS Code: Cmd/Ctrl+Shift+P → "Dev Containers: Rebuild Container"
# Or manually:
docker system prune -f
# Then reopen in containerThe setup automatically handles kernel headers, but if you encounter issues:
# Check current kernel
uname -r
# Install headers manually (inside container)
sudo apt-get update
sudo apt-get install -y linux-headers-$(uname -r)# Inside the development container:
cd /workspaces/jsonk # or your workspace path
# Build all modules
make
# Install the modules (optional)
sudo make install
# Load the core module
sudo make load# Test basic functionality
make test-basic
# Run performance benchmarks
make test-perf
# Test atomic patching
make test-atomic
# View results
dmesg | tail -50#include "include/jsonk.h"
// Parse JSON string
const char *json_str = "{\"name\":\"test\",\"value\":42}";
struct jsonk_value *json = jsonk_parse(json_str, strlen(json_str));
// Access object members
struct jsonk_member *name_member = jsonk_object_find_member(&json->u.object, "name", 4);
if (name_member && name_member->value->type == JSONK_VALUE_STRING) {
printk("Name: %s\n", name_member->value->u.string.data);
}
// Serialize back to string
char buffer[1024];
size_t written;
int ret = jsonk_serialize(json, buffer, sizeof(buffer), &written);
if (ret == 0) {
printk("JSON: %.*s\n", (int)written, buffer);
}
// Clean up with reference counting
jsonk_value_put(json);// Original JSON
const char *target = "{\"name\":\"test\",\"value\":42}";
// Patch to apply
const char *patch = "{\"value\":100,\"new_field\":\"added\"}";
char result[1024];
size_t result_len;
int ret = jsonk_apply_patch(target, strlen(target),
patch, strlen(patch),
result, sizeof(result), &result_len);
if (ret == JSONK_PATCH_SUCCESS) {
printk("Patched JSON: %.*s\n", (int)result_len, result);
// Result: {"name":"test","value":100,"new_field":"added"}
}// Access nested values using dot notation
const char *json_str = "{\"user\":{\"profile\":{\"name\":\"Mehran\",\"age\":30}}}";
struct jsonk_value *json = jsonk_parse(json_str, strlen(json_str));
// Get nested value
struct jsonk_value *name = jsonk_get_value_by_path(json, "user.profile.name", 17);
if (name && name->type == JSONK_VALUE_STRING) {
printk("Name: %s\n", name->u.string.data);
}
// Set nested value (creates intermediate objects if needed)
struct jsonk_value *new_role = jsonk_value_create_string("admin", 5);
jsonk_set_value_by_path(json, "user.profile.role", 17, new_role);
jsonk_value_put(new_role);
jsonk_value_put(json);// JSONK is not thread-safe - callers must handle synchronization
static struct jsonk_value *shared_json = NULL;
static DEFINE_SPINLOCK(json_lock);
void safe_json_update(const char *path, struct jsonk_value *value) {
unsigned long flags;
spin_lock_irqsave(&json_lock, flags);
if (shared_json) {
jsonk_set_value_by_path(shared_json, path, strlen(path), value);
}
spin_unlock_irqrestore(&json_lock, flags);
}
void safe_json_read(char *buffer, size_t buffer_size, size_t *written) {
unsigned long flags;
struct jsonk_value *json_ref = NULL;
spin_lock_irqsave(&json_lock, flags);
if (shared_json) {
json_ref = jsonk_value_get(shared_json); // Get reference
}
spin_unlock_irqrestore(&json_lock, flags);
if (json_ref) {
jsonk_serialize(json_ref, buffer, buffer_size, written);
jsonk_value_put(json_ref); // Release reference
}
}struct jsonk_value *jsonk_parse(const char *json_str, size_t json_len);Parse a JSON string into an in-memory structure.
Parameters:
json_str: JSON string to parsejson_len: Length of JSON string
Returns: Pointer to parsed JSON value or NULL on error
int jsonk_serialize(struct jsonk_value *value, char *buffer, size_t buffer_size, size_t *written);Serialize a JSON value structure to a string.
Parameters:
value: JSON value to serializebuffer: Output bufferbuffer_size: Size of output bufferwritten: Pointer to store actual bytes written
Returns: 0 on success, negative error code on failure
struct jsonk_value *jsonk_value_get(struct jsonk_value *value);Get a reference to a JSON value (increment reference count).
void jsonk_value_put(struct jsonk_value *value);Release a reference to a JSON value (decrement reference count, free when zero).
int jsonk_apply_patch(const char *target, size_t target_len,
const char *patch, size_t patch_len,
char *result, size_t result_max_len, size_t *result_len);Apply a JSON patch to a target buffer atomically.
Returns:
JSONK_PATCH_SUCCESS: Operation completed successfullyJSONK_PATCH_NO_CHANGE: No changes were madeJSONK_PATCH_ERROR_*: Various error conditions
struct jsonk_value *jsonk_value_create_string(const char *str, size_t len);struct jsonk_value *jsonk_value_create_number(const char *str, size_t len);struct jsonk_value *jsonk_value_create_boolean(bool val);struct jsonk_value *jsonk_value_create_null(void);int jsonk_object_add_member(struct jsonk_object *obj, const char *key, size_t key_len, struct jsonk_value *value);struct jsonk_member *jsonk_object_find_member(struct jsonk_object *obj, const char *key, size_t key_len);int jsonk_object_remove_member(struct jsonk_object *obj, const char *key, size_t key_len);int jsonk_array_add_element(struct jsonk_array *arr, struct jsonk_value *value);struct jsonk_value *jsonk_get_value_by_path(struct jsonk_value *root, const char *path, size_t path_len);Get a value using a dot-separated path (e.g., "user.profile.name").
Parameters:
root: Root JSON objectpath: Dot-separated path stringpath_len: Length of path string
Returns: Pointer to found value or NULL if not found
int jsonk_set_value_by_path(struct jsonk_value *root, const char *path, size_t path_len, struct jsonk_value *value);Set a value using a dot-separated path. Creates intermediate objects if they don't exist.
Parameters:
root: Root JSON objectpath: Dot-separated path stringpath_len: Length of path stringvalue: Value to set (will be deep copied)
Returns: 0 on success, negative error code on failure
- Parsing: Single-pass, O(n) complexity
- Memory: Efficient memory management with reference counting
- Patching: Atomic operations, copy-on-write semantics
- Serialization: Direct buffer writing, no intermediate allocations
The Makefile provides complete build and test targets:
make all # Build all modules
make clean # Clean build files
make install # Install modules system-wide
make uninstall # Remove installed modulesmake load # Load the jsonk core module
make unload # Unload the jsonk core modulemake test-basic # Run basic usage examples
make test-perf # Run comprehensive performance tests
make test-atomic # Run atomic patching tests
# Individual module loading
make load-basic # Load basic usage module
make load-perf # Load performance test module
make load-atomic # Load atomic test module
# Individual module unloading
make unload-basic # Unload basic usage module
make unload-perf # Unload performance test module
make unload-atomic # Unload atomic test module# Complete test workflow
make clean && make
make test-basic
make test-perf
make test-atomic
# View test results
dmesg | tail -100To use JSONK in your kernel module:
- Include the header:
#include "path/to/jsonk.h"- Add dependency in your Makefile:
# Ensure jsonk is loaded first
obj-m += your_module.o
your_module-objs := your_source.o- Declare dependency:
MODULE_SOFTDEP("pre: jsonk");JSONK supports both additive and removal operations through JSON patching:
- Add new fields: Include them in the patch object
- Update existing fields: Provide new values in the patch object
- Merge objects: Nested objects are recursively merged
Fields are removed when the patch contains:
nullvalues- Empty strings (
"") - Empty objects (
{}) - Empty arrays (
[])
// Adding and updating
const char *patch1 = "{\"new_field\":\"value\",\"existing_field\":\"updated\"}";
// Removing fields
const char *patch2 = "{\"remove_me\":null,\"also_remove\":\"\"}";
// Mixed operations
const char *patch3 = "{\"add\":\"new\",\"update\":\"changed\",\"remove\":null}";
// Nested operations
const char *patch4 = "{\"user\":{\"name\":\"updated\",\"old_field\":null}}";- Maximum nesting depth: 32 levels (configurable via
JSONK_MAX_DEPTH) - Maximum object members: 1000 per individual object (configurable via
JSONK_MAX_OBJECT_MEMBERS) - Maximum array elements: 10000 per individual array (configurable via
JSONK_MAX_ARRAY_SIZE) - Number precision: Limited to 64-bit integers and basic fractional support
- Unicode handling: Unicode escape sequences stored literally for kernel efficiency
- Memory limits: Built-in limits to prevent DoS attacks in kernel space
JSONK is not thread-safe by design. Callers must handle synchronization when using JSONK functions from multiple threads.
// Example with spinlock
static DEFINE_SPINLOCK(json_lock);
spin_lock(&json_lock);
ret = jsonk_apply_patch(target, target_len, patch, patch_len,
result, result_len, &written);
spin_unlock(&json_lock);JSONK uses reference counting for memory safety and UAF protection:
- All JSON values start with a reference count of 1
- Use
jsonk_value_get()to increment the reference count - Use
jsonk_value_put()to decrement the reference count - Memory is automatically freed when the reference count reaches 0
// Create a JSON value (starts with refcount = 1)
struct jsonk_value *json = jsonk_parse(json_str, len);
// Get additional reference (refcount = 2)
struct jsonk_value *json_ref = jsonk_value_get(json);
// Release references (refcount decrements, frees when reaches 0)
jsonk_value_put(json_ref); // refcount = 1
jsonk_value_put(json); // refcount = 0, memory freedReference counting provides protection against use-after-free vulnerabilities in multi-threaded environments:
// Thread 1: Get reference before using
struct jsonk_value *safe_ref = jsonk_value_get(shared_json);
// Use safe_ref...
jsonk_value_put(safe_ref);
// Thread 2: Can safely free without affecting Thread 1
jsonk_value_put(shared_json); // Only frees when all references releasedThis project is licensed under the GNU General Public License v2.0 - see the LICENSE file for details.
For issues, questions, or contributions, please open an issue on the project repository.