What I love about this author's work is that they're usually single-file libraries in ANSI C or Lua with focused scope, easy-to-use interface, and good documentation. And free software license. Aside from the posted project, some I like are:
- log.c - A simple logging library implemented in C99
- microui - A tiny immediate-mode UI library
- fe - A tiny, embeddable language implemented in ANSI C
- microtar - A lightweight tar library written in ANSI C
- cembed - A small utility for embedding files in a C header
- ini - A tiny ANSI C library for loading .ini config files
- json.lua - A lightweight JSON library for Lua
- lite - A lightweight text editor written in Lua
- cmixer - Portable ANSI C audio mixer for games
- uuid4 - A tiny C library for generating uuid4 strings
I wouldn’t rate those as very serious issues for this project. They’ll only be triggered if there are over MAX_INT lines or depth levels in the input. Yes, an attacker might be able to do that, but you’d have to put that input in a memory buffer to call this code. On many smaller systems, that will OOM.
Skimming the code, they also are loose in parsing incorrect json, it seems:
static bool sj__is_number_cont(char c) {
return (c >= '0' && c <= '9')
|| c == 'e' || c == 'E' || c == '.' || c == '-' || c == '+';
}
case '-': case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
res.type = SJ_NUMBER;
while (r->cur != r->end && sj__is_number_cont(*r->cur)) { r->cur++; }
break;
that seems to imply it treats “00.-E.e-8..7-E7E12” as a valid json number.
case '}': case ']':
res.type = SJ_END;
if (--r->depth < 0) {
r->error = (*r->cur == '}') ? "stray '}'" : "stray ']'";
goto top;
}
r->cur++;
break;
I think that means the code finds [1,2} a valid array and {"foo": 42] a valid struct (maybe, it even is happy with [1,2,"foo":42})
Those, to me, seem a more likely attack vector. The example code, for example, calls atoi on something parsed by the first piece of code.
⇒ I only would use this for parsing json config files.
Being tiny is one thing, but the json grammar isn’t that complex. They could easily do a better job at this without adding zillions of lines of code.
this is really nice. i also _must_ use it because my initials are S.J H.. :').
on the more code side, love this, been looking to implement a simple json parser for some projects but this is small enough i can study it and either learn what i need or even use it. lovely!
Neat! Might look at this next time I need a JSON parser in C.
I've been using cJSON[0] for years now and am pretty happy with it. I had used wjelement[1] before that but ran into a few issues and eventually moved away from it (can't recall why exactly its been so long.)
I really enjoy these simple libraries, even though they are too flawed to be used for anything serious. There's great freedom in just drilling down to the basics, ignoring all the complexities, and just writing code that'll probably work most of the time.
This is rather lenient. There's not anything wrong with that (although perhaps it should be noted for people that will use it without looking at the code), but it's the main reason this can be so small. Using their demo in the readme:
A recommendation: we use rxi's json.lua at Planimeter, but upon benchmarking JSON implementations across all major languages, it was found to be the slowest by an order of magnitude when reading very large numbers of JSON files.
So if you can, try and at least use LuaJIT, which when using json.lua seems to bring it back down into range with other performant languages, or jump down into LuaJIT and use Sj.h there, through the C FFI or just simdjson.
json.lua is great for when you're restricted in some ways to use a pure Lua implementation, though. It's the de facto solution.
As far as I can see, the library is encoding agnostic (eg ASCII, Extended ASCII and UTF-8 should all work fine - and UTF-8 is the defacto standard UNICODE encoding since the late 1990s.
Does C99 specify that this structure would be 0 initialized by default, or is this line missing a = { 0 }? - https://github.com/rxi/sj.h/blob/5cb5df45c8c37fd8c2322026a11... - it reads to me like r->depth might be randomly initialized and equal to depth by random chance on the first iteration of the sj__discard_until loop
This is quite neat. I wrote a similar library for no-alloc JSON parsing, but never had use for it. This does actual parsing, though; my approach is to just navigate through a JSON tree.
What is the purpose of declaring an sj_Reader object with an sj_read method if you don't support lazy parsing? Seems like you could tighten up the code by combining the declaration and parsing into one subroutine.
25 comments
[ 4.1 ms ] story [ 51.7 ms ] threadhttps://github.com/nst/JSONTestSuite
- log.c - A simple logging library implemented in C99
- microui - A tiny immediate-mode UI library
- fe - A tiny, embeddable language implemented in ANSI C
- microtar - A lightweight tar library written in ANSI C
- cembed - A small utility for embedding files in a C header
- ini - A tiny ANSI C library for loading .ini config files
- json.lua - A lightweight JSON library for Lua
- lite - A lightweight text editor written in Lua
- cmixer - Portable ANSI C audio mixer for games
- uuid4 - A tiny C library for generating uuid4 strings
They're either written with a different use case in mind, or a complex mess of abstractions; often both.
It's not a very difficult problem to solve if you only write exactly what you need for your specific use case.
https://github.com/rxi/sj.h/blob/eb725e0858877e86932128836c1...
https://github.com/rxi/sj.h/blob/eb725e0858877e86932128836c1...
https://github.com/rxi/sj.h/blob/eb725e0858877e86932128836c1...
Certain inputs can therefore trigger UB.
Skimming the code, they also are loose in parsing incorrect json, it seems:
that seems to imply it treats “00.-E.e-8..7-E7E12” as a valid json number. I think that means the code finds [1,2} a valid array and {"foo": 42] a valid struct (maybe, it even is happy with [1,2,"foo":42})Those, to me, seem a more likely attack vector. The example code, for example, calls atoi on something parsed by the first piece of code.
⇒ I only would use this for parsing json config files.
Being tiny is one thing, but the json grammar isn’t that complex. They could easily do a better job at this without adding zillions of lines of code.
on the more code side, love this, been looking to implement a simple json parser for some projects but this is small enough i can study it and either learn what i need or even use it. lovely!
Im still impressed and might use it, but just noting this.
I've been using cJSON[0] for years now and am pretty happy with it. I had used wjelement[1] before that but ran into a few issues and eventually moved away from it (can't recall why exactly its been so long.)
[0] https://github.com/DaveGamble/cJSON [1] https://github.com/netmail-open/wjelement
So if you can, try and at least use LuaJIT, which when using json.lua seems to bring it back down into range with other performant languages, or jump down into LuaJIT and use Sj.h there, through the C FFI or just simdjson.
json.lua is great for when you're restricted in some ways to use a pure Lua implementation, though. It's the de facto solution.
https://github.com/lelanthran/libxcgi/blob/master/library/sr...
https://github.com/lelanthran/libxcgi/blob/master/library/sr...
Which does much more in 200 lines of C89 in a single header