wolfSSL releases a new product; wolfCOSE a zero alloc C embbedded COSE stack
github.com
github.com
I love me some CBOR, but Carl isn’t very adventurous in deviating from JSON (rightfully!) so I don’t expect a lot new in COSE if you have worked with JOSE.
Other than the tagged data types, the main inconpatibiiiry in CBOR to JSON is that CBOR map keys can be integers and in JSON must be strings.
A good zero-order classifier for "is this signing format a dumpster fire" is whether the spec mentions canonical encodings.
[1] https://github.com/EmilStenstrom/justhtml
1. .text size without clarifying the architecture, flags, and compiler is meaningless unless it's all rodata (and it's not)
2. Saying it takes 0 .bss and .data just means it allocates everything elsewhere and that can be helpful to know. Of course in compilation that'll also be dependent on how and for what it's built. To say it's zero alloc is incorrect or at best misleading. Here's a line of code that allocates a ton of stuff on the stack: https://github.com/wolfSSL/wolfCOSE/blob/b90b34abcba90aa7b8a... (previously pointed to another line but it was diluting my thesis). Anyone in embedded who's had to increase stack size to use a fancy function knows what I'm talking about. I'm looking at you, sscanf. Some of this code will allocate hundreds if not low thousands of bytes onto the stack. Which is maybe fine but don't say it's zero alloc just because it's all on the stack.
(Note MSVC has alloca—and Microsoft’s own libraries in the past have done unwise things with it—so the safety argument for the lack of support does not fly.
[1] https://herbsutter.com/2012/05/03/reader-qa-what-about-vc-an...
I’ve thrown all my projects i have written in c for posix systems in the last 15 years against msvc and had only one project which had an issue. Because a dep used VLAs.
I think it would be better if they made you pass a struct with those arrays and such as members, then you get to chose if you want to put it in global memory to ensure they're available or if you take your chance on a local stack instance.
Or what do you mean with sizeable arrays?
It contains a bunch of configurable parameters, including the 64-byte size of those buffers.
Based on those configurable parameters, which also impose limits on the depth of procedure calls, it is possible to compute which will be the maximum space that will be needed in a stack. Thus in an embedded system it would be possible to guarantee that the stack size is not exceeded.
By changing the values of those configuration parameters, it should be possible to tune the size of the stack, with the price that with a lower space available in the stack it may become impossible to decode certain more complex messages.
The line you point at creates a single local pointer variable which is used in a tight loop; I don't see why won't it stay entirely in a register.
I'm not a real embedded developer though; last time I worked as one I worked on 8-bit devices. Maybe things changed since then.
My bigger point was that no malloc should be called "stack allocated" or some other more technically correct term. That tells me "hey if you run this code and something goes haywire, check your stack isn't corrupted" because 9 times out of ten for me that's the problem.
Most companies buying anything from WolfSSL will already be using a script or toolchain flags to validate stack usage. And if they don't, even embedded toolchains generally support canaries these days.
Their README states "zero dynamic allocation: all operations use caller-provided buffers" and "Full COSE lifecycle in ~<1KB RAM (excluding wolfCrypt internals)", so I assume their stack usage is low too, because you (the caller) will own and have to allocate all buffers yourself
Nobody is going to write code with exclusively global variables and GOTO instead of function calls, in order to use zero stack. And if they did, the other claim (no .data or .bss) would be untrue.
I'm not sure how you misinterpreted "Zero dynamic allocation" to mean "not even stack variables", because nobody would read it that way, and I don't think anyone sane would use software that promised it used no stack.
All data must be allocated in the program invoking procedures from the library, and passed as actual parameters.
You are right about the .text size being dependent on architecture, flags and compiler, but these dependencies may at most double or triple the size. They will certainly not make the size ten times greater. So with a maximum size of 25 kB, I expect that the maximum size will be under 100 kB on any combinations of architecture, flags and compilers.
I do not understand exactly what you mean about "unless it's all rodata". Depending on the architecture, flags and compiler, the constants may be allocated in separate sections, like ".rodata", or they may also be allocated in the same ".text" section with the executable code. The latter choice is typically superior on the CPUs that have relative addressing, like x86_64.
This is what you meant, that it is not clear whether the quoted ".text" size also includes the constants, or not?
I do not think that such a library includes a great amount of constants, so it is likely that adding them or not does not change much the size.
At the line pointed by you, the size of each of the 2 allocated buffers is 64 bytes. The buffer sizes and other parameters that determine the maximum amount of stack usage are defined in "wolfcose.h". It appears that is possible to tune the amount of stack needed, as a tradeoff with the complexity of the messages that can be decoded.
I agree that "Zero dynamic allocation" in the README is not really correct, because they meant "zero allocation in the heap".
Nevertheless, this cannot cause confusions, because any programmer should be aware that a claim of no dynamic allocation of any kind is typically impossible, because almost all functions or procedures must allocate some variables in the stack, with very rare exceptions where there are so few local variables that they may be allocated only inside registers. On x86_64, zero allocation in the stack is completely impossible, because at least the return address must be allocated in the stack.