When you're deep in kernel development and need to handle structured data, you quickly realize that the kernel's ecosystem has a glaring omission: there's virtually no mature JSON processing library available. This became painfully apparent during a recent project where I needed reliable JSON handling within a kernel module.
The options were limited. I could either cobble together a basic parser from scratch, risking bugs and incomplete functionality, or find some way to bridge user-space libraries with kernel code, introducing complexity and performance overhead. Neither felt right for production code that needed to be both fast and bulletproof.
After searching through the available kernel JSON libraries, the landscape was surprisingly empty. Most implementations were either academic exercises or incomplete attempts that handled only basic cases. None offered the robustness needed for real-world kernel module development. This gap led to the creation of JSONK.
The Kernel Space Reality
Working in kernel space means playing by different rules. You don't have the luxury of standard library functions or the safety net of process boundaries. When your code crashes, it doesn't just kill a process - it can bring down the entire system. Memory management becomes critical because leaks accumulate over time and can destabilize the machine. Concurrency is everywhere, with multiple threads potentially accessing your data structures simultaneously.
These constraints make JSON processing particularly challenging. User-space libraries assume they can allocate memory freely, handle errors by throwing exceptions, and rely on process isolation for safety. None of these assumptions hold in kernel space.
The existing kernel JSON libraries I found reflected these challenges poorly. Most were incomplete implementations that worked for simple cases but failed on edge conditions. Few handled concurrent access properly, and none provided the atomic update capabilities that kernel modules often need for maintaining consistent state.
Building JSONK
JSONK emerged from these practical needs. Rather than building yet another minimal parser, I focused on creating a library that could handle real-world kernel module requirements: full RFC 8259 compliance, robust error handling, safe memory management, and atomic operations for consistent updates.
The design started with memory management. In kernel space, every allocation matters, and cleanup must be guaranteed. JSONK uses reference counting to ensure that JSON values are properly freed when no longer needed, even in complex scenarios where multiple parts of the code hold references to the same data. This prevents the use-after-free vulnerabilities that can crash the kernel.
Core Features
The library provides several key capabilities designed for kernel space:
- RFC 8259 Compliance: Complete JSON specification support including all data types, proper string escaping, unicode sequences, and number parsing
- Atomic JSON Patching: Apply partial updates to JSON objects with rollback safety - either all changes succeed or none are applied
- Reference Counting: Automatic memory management to prevent use-after-free vulnerabilities
- Path-Based Access: Navigate nested structures using dot notation like "user.profile.name"
- Memory Safety: Built-in limits to prevent DoS attacks in kernel space
For parsing, the library implements a single-pass algorithm that builds the JSON structure directly without intermediate representations. This approach minimizes memory allocations and provides predictable performance characteristics. The parser handles all JSON data types correctly, including proper string escaping, number parsing with scientific notation, and nested structures with configurable depth limits.
One feature that sets JSONK apart is atomic JSON patching. This allows you to apply partial updates to JSON objects with rollback safety - either all changes succeed, or none are applied. This capability is crucial for kernel modules that need to maintain consistent configuration state or handle concurrent updates safely.
Performance in Practice
Testing JSONK on Linux 6.8.0 reveals performance characteristics that work well for kernel space applications:
- 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)
Small JSON documents around 833 bytes with 10 objects parse at 425,000 operations per second, delivering 337 MB/s throughput. This performance makes the library suitable for high-frequency operations or real-time scenarios where consistent sub-millisecond response times are required.
As document size increases to around 8KB with 100 objects, the library maintains excellent throughput at 859 MB/s despite the lower operation rate of 13,800 operations per second. This demonstrates efficient scaling as the parser handles larger, more complex structures. For large documents around 1MB with 200 objects, the library achieves 972 MB/s throughput at 1,120 operations per second, showing that bulk data processing remains highly efficient.
The scalability metrics reveal excellent per-element performance, ranging from 53 to 220 nanoseconds per element as document size increases. This consistent scaling behavior makes performance predictable across different workload sizes.
JSON serialization performs exceptionally well at 5.94 million operations per second with 872 MB/s throughput. The atomic patching operations achieve 827,000 operations per second, which is excellent considering the additional safety guarantees and complexity involved.
Security Considerations
Security in kernel space requires careful attention to several attack vectors. JSONK addresses these concerns through multiple layers of protection.
The library implements strict input validation to prevent malformed JSON from causing buffer overflows or memory corruption. All string operations use bounded functions, and buffer sizes are validated before any memory operations. The parser rejects JSON that exceeds configured limits for nesting depth, object member counts, and array sizes, preventing resource exhaustion attacks.
Memory management uses reference counting to prevent use-after-free vulnerabilities, which are particularly dangerous in kernel space where they can lead to privilege escalation. The atomic patching system ensures that partial updates cannot leave data structures in inconsistent states that might be exploitable.
Input sanitization handles control characters and validates Unicode escape sequences to prevent injection attacks. The library also implements bounds checking on all array and object access operations to prevent buffer overruns that could corrupt kernel memory.
For denial-of-service protection, JSONK enforces limits on parsing time and memory usage. Large or deeply nested JSON documents are rejected before they can consume excessive system resources. These limits are configurable but have safe defaults that prevent most resource exhaustion scenarios.
Real-World Integration
Using JSONK in a kernel module is straightforward. The library provides a clean API that feels familiar to developers who have worked with JSON libraries in other contexts, while respecting kernel space conventions.
Basic Usage Example
#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);
}
// Apply a patch atomically
const char *patch = "{\"value\":100,\"new_field\":\"added\"}";
char result[1024];
size_t result_len;
int ret = jsonk_apply_patch(json_str, strlen(json_str),
patch, strlen(patch),
result, sizeof(result), &result_len);
// Clean up with reference counting
jsonk_value_put(json);
The reference counting model means you explicitly manage object lifetimes using jsonk_value_get()
and jsonk_value_put()
functions. This might seem cumbersome compared to garbage-collected environments, but it provides the predictable behavior that kernel code requires. You know exactly when memory is allocated and freed, which is essential for debugging and ensuring system stability.
Concurrent Access Pattern
For concurrent access, JSONK takes a practical approach. Rather than implementing internal locking that might not match your module's synchronization strategy, the library requires you to handle synchronization yourself:
static struct jsonk_value *shared_state = NULL;
static DEFINE_SPINLOCK(state_lock);
void update_shared_state(const char *path, struct jsonk_value *new_value) {
unsigned long flags;
spin_lock_irqsave(&state_lock, flags);
if (shared_state) {
jsonk_set_value_by_path(shared_state, path, strlen(path), new_value);
}
spin_unlock_irqrestore(&state_lock, flags);
}
struct jsonk_value *get_shared_state_copy(void) {
unsigned long flags;
struct jsonk_value *copy = NULL;
spin_lock_irqsave(&state_lock, flags);
if (shared_state) {
copy = jsonk_value_get(shared_state); // Get reference
}
spin_unlock_irqrestore(&state_lock, flags);
return copy; // Caller must call jsonk_value_put(copy)
}
This gives you complete control over locking granularity and avoids potential deadlocks from conflicting lock hierarchies.
The atomic patching feature proves particularly useful for configuration management. You can parse a configuration JSON, apply updates from user space or other modules, and ensure that either all changes take effect or none do. This prevents the intermediate states that could leave your module in an inconsistent configuration.
Design Constraints and Trade-offs
JSONK makes deliberate trade-offs for kernel space operation. The library imposes limits on nesting depth, object member counts, and array sizes. These limits prevent resource exhaustion attacks and ensure predictable memory usage, but they might constrain applications that need to process arbitrarily complex JSON structures.
Current Limitations
- Maximum nesting depth: 32 levels (configurable)
- Maximum object members: 1000 per object (configurable)
- Maximum array elements: 10000 per array (configurable)
- Number precision: 64-bit integers and basic decimal support
- Unicode handling: Escape sequences stored literally for kernel efficiency
Number handling focuses on 64-bit integers and basic decimal support rather than arbitrary precision arithmetic. This covers the vast majority of kernel use cases while avoiding the complexity and performance overhead of full-featured number processing. Similarly, Unicode escape sequences are stored literally rather than being converted to UTF-8, which maintains compatibility while avoiding complex character encoding logic in kernel space.
The library doesn't include features like JSON Schema validation or advanced path expressions. These capabilities could be added, but they would increase complexity and memory usage for functionality that most kernel modules don't need. The current feature set focuses on the core operations that kernel code actually requires.
Integration and Build System
JSONK is designed for easy integration with existing kernel module development workflows:
Module Dependencies
# In your module's Makefile
obj-m += your_module.o
your_module-objs := your_source.o
# Declare dependency
MODULE_SOFTDEP("pre: jsonk");
Build and Test Workflow
# Build and test workflow
make clean && make
make test-basic # Basic functionality tests
make test-perf # Performance benchmarks
make test-atomic # Atomic patching tests
# View results
dmesg | tail -50
The build system provides targets for compilation, testing, and module management, making it easy to integrate JSONK into existing development workflows.
Current Status and Availability
JSONK is available now under the GPL-2.0 license at https://github.com/mehrantsi/jsonk. The repository includes the complete library source, examples, and a full test suite that validates both functionality and performance. The build system provides targets for compilation, testing, and module management.
The library has been tested on Linux 6.8.0 and should work on any reasonably recent kernel version. The code follows kernel coding conventions and has been designed to integrate cleanly with existing kernel module development workflows.
Feedback and Contributions
The project welcomes feedback and contributions from the kernel development community. Real-world usage will undoubtedly reveal areas for improvement and additional features that would benefit the broader ecosystem. But even in its current form, JSONK provides capabilities that simply weren't available before in kernel space, opening up new possibilities for kernel module development.
Top comments (0)