Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dtls_debug.h: explicitly cast macro parameter to size_t. #217

Merged
merged 1 commit into from
Mar 18, 2024

Conversation

boaks
Copy link
Contributor

@boaks boaks commented Dec 7, 2023

No description provided.

@OlegHahm
Copy link

As far as I understand the test results, the Windows toolchain does not support %zu. Should we use %lu instead then? May waste some bytes on some platforms but that shouldn't be an issue for debug output.

@boaks
Copy link
Contributor Author

boaks commented Dec 21, 2023

No need. PR #218 or #216 will fix it.

(I will ask Olaf an Jon again, if we could decide the way to go.)

@obgm
Copy link
Contributor

obgm commented Dec 22, 2023

I would refrain from casting to size_t in any macro because we do not know what the input parameters are. Keep in mind that the size of size_t depends on the target platform, and that people sometimes use int for counting things. A type cast therefore should occur where the parameter is passed to this macro.
Anyway, PR #218 is merged know, so the particular issue of not having %zu should be fixed.

@boaks
Copy link
Contributor Author

boaks commented Dec 22, 2023

I'm not sure, if I get it.

The macros are all dumping an array of bytes with a certain length.
The default calls:

void dtls_dsrv_hexdump_log(log_t level, const char *name, const unsigned char *buf, size_t length, int extend); which casts the length implicit to size_t).

so, if we want similar behavior, we need to add that cast to the specific versions for zephyr and riot.

Or did I miss something?

@boaks
Copy link
Contributor Author

boaks commented Dec 27, 2023

I've checked this again, and I still don't see the point to "refrain from that cast to size_t".

What this PR is fixing is the assumption in the LOG_DBG/LOG_DEBUG, where the length is passed to "%zu". I don't see, why that should harm. On the other side, if this is required to be done at the call, then this will introduce an error source, if not done. That would also cause to fix several locations within tinydtls itself, where we then need to ensure, that "%zu" gets passed the proper type.

@mrdeep1
Copy link
Contributor

mrdeep1 commented Dec 27, 2023

I'm inclined to agree with obgm, based on the law of unintended consequences.

As I read the code, dtls_dsrv_hexdump_log() is always there (apart from CONTIKI), so why not have the stub functions in dtls_debug.c handling Zephyr, RIOT etc. which then call the dedicated functions as the #define currently does.

@boaks
Copy link
Contributor Author

boaks commented Dec 27, 2023

Using macros in Zephyr (and RIOT?) allows to remove the logging code of some logging levels completely, so that they don't consume storage.

based on the law of unintended consequences.

The code is

#define dtls_debug_hexdump(name, buf, length) { if (LOG_DEBUG <= LOG_LEVEL) { LOG_DEBUG("-= %s (%zu bytes) =-\n", name, (size_t) length); od_hex_dump(buf, length, 0); }}

so I see the "unintended consequences" in not casting length.

@obgm
Copy link
Contributor

obgm commented Dec 28, 2023

so I see the "unintended consequences" in not casting length.

The problem is that dtls_debug_hexdump is part of the public API. And here is what will happen in user applications:

int length = some_custom_function();
const char *data = some_other_custom_function;

dtls_debug_hexdump("foo", data, length);

This happens all the time. Really.

@boaks
Copy link
Contributor Author

boaks commented Dec 28, 2023

This happens all the time. Really.

Sorry, I may be blind and/or stupid.

What makes the difference of

int length = some_custom_function();
const char *data = some_other_custom_function;
LOG_DEBUG("-= %s (%zu bytes) =-\n", name, (size_t) length); 
int length = some_custom_function();
const char *data = some_other_custom_function;
LOG_DEBUG("-= %s (%zu bytes) =-\n", name, length); 

In the second case, we may get a warning, or the printed result is somehow random.
The first case works in my experience.

@boaks
Copy link
Contributor Author

boaks commented Dec 28, 2023

dtls_debug_hexdump("foo", data, length);

As I wrote above, this is expanded in a call to

void dtls_dsrv_hexdump_log(log_t level, const char *name, const unsigned char *buf, size_t length, int extend)

which implicitly casts the length to size_t.
Why is then the added cast so bad?
And again, if this is not added, we need to fix several locations with tinydtls itself, because there we pass in parameter, which are not "size_t".

@mrdeep1
Copy link
Contributor

mrdeep1 commented Dec 28, 2023

An unintended consequence is when an unexpected negative int value gets expanded into a huge number when cast to a size_t which then causes the app to blow up when doing the following hex dump. The safer way is make sure all the callers have a size_t length parameter.

@boaks
Copy link
Contributor Author

boaks commented Dec 28, 2023

Therefore the parameter length is only casted for the "LOG_DEBUG/LOG_DBG - %zu" and not for the dump call itself (neither "LOG_HEXDUMP_DBG" nor "od_hex_dump").

In order to test the "protection" of not casting, I added

  dtls_debug_hexdump("test", data, -1);

to dtls.c/dtls_handle_message and run a build on unix/posix (ubuntu 22.04).
No warnings at compile time. And

DEBG test: (18446744073709551615 bytes):

with crash at runtime.

if -1 is dangerous, it's hard to see the protection to not cast the parameter which is passed to "%zu".

@mrdeep1
Copy link
Contributor

mrdeep1 commented Dec 28, 2023

with crash at runtime.
if -1 is dangerous, it's hard to see the protection to not cast the parameter which is passed to "%zu".

I'm guessing the crash is in the hexdump logic, so the invokers of dtls_debug_hexdump() must make sure that there is no possibility of a large length (or negative value) getting passed in, or the dtls_debug_hexdump() does additional sanity checking - preferable.

@boaks
Copy link
Contributor Author

boaks commented Dec 28, 2023

the crash is in the hexdump logic

Sure.

must make sure that there is no possibility of a large length (or negative value) getting passed in,

Exactly. And the same prerequisite about the value of length applies for the macro with the cast to size_t.
With the current implementation, that added cast doesn't add an error source, that error source is already there.
But it fixes the error for "LOG_DEBUG/LOG_DBG - %zu".

If it's then considered, that the hexdump should be limited, e.g. to 1k, then this is an additional issue and with that we may adapt the macro again.

@obgm
Copy link
Contributor

obgm commented Dec 28, 2023

which implicitly casts the length to size_t.

In my example, length is of type int. The type of size_t depends on the target platform. On linux, this is typically unsigned long (= 8 bytes on 64-bit architectures). AFAIK, on windows, it is unsigned long long. The issue I see is with this parameter being passed to the varargs of printf. I have not checked whether or not passing a shorter type for %zu will cause issues but I prefer to be conservative and pass the correct data type.

we need to fix several locations with tinydtls itself, because there we pass in parameter, which are not "size_t".

Yes but this is not a problem, we just need to do that. But for every cast we have to answer this question: "Is it correct to cast this type to size_t?" If not, we need to change the code because otherwise, it will likely break something in the future. This is why I have a very strong opinion on doing type casts at the origin of the "wrong types".

@boaks
Copy link
Contributor Author

boaks commented Dec 28, 2023

Which type does "%zu" support? Isn't it "size_t"?
At least according C99 it is.

@obgm
Copy link
Contributor

obgm commented Dec 28, 2023

Correct: size_t.

@boaks
Copy link
Contributor Author

boaks commented Dec 28, 2023

Then this discussion seems to be based on what ever, but not the code change contained in this PR.
As I demonstrated above, cast or not cast will not prevent the assumed error. Nor does it help with warnings (at least I didn't get warnings).
But the cast will fix the LOG_DEBUG("-= %s (%zu bytes) =-\n", name, length); though it ensures, that "length" is cast to the expected type "size_t".

@mrdeep1
Copy link
Contributor

mrdeep1 commented Dec 29, 2023

Ok - unintended consequence.

 int a = -1;
 int b = 5;
 char buf[] = "abcdef";

 dtls_debug_hexdump("test", buf, a+b);

which then translates (using your fix)

LOG_DEBUG("-= %s (%zu bytes) =-\n", buf, (size_t)a + b);

and so LOG_DEBUG is passed a large number rather than 4 (it is possible the resultant sum may overflow and wrap).

Another unintended consequence

 int a = 6
 int * b = &a;
 char buf[] = "abcdef";

 dtls_debug_hexdump("test", buf, *b);

which then translates (using your fix)

LOG_DEBUG("-= %s (%zu bytes) =-\n", buf, (size_t)*b);

No idea here as to what is typed to size_t.

Passed parameters should be wrapped with ( ) as in

#define dtls_debug_hexdump(name, buf, length) { LOG_DBG("%s (%zu bytes):", (name), (size_t)(length)); LOG_HEXDUMP_DBG((buf), (length), (name)); }

I agree that typecasting (if done properly) will get rid of your warnings generated by some compilers.

Signed-off-by: Achim Kraus <achim.kraus@cloudcoap.net>
@boaks boaks force-pushed the fix_size_t_macros branch from 837d0e1 to 8267a24 Compare December 29, 2023 15:10
Comment on lines +132 to +133
#define dtls_debug_hexdump(name, buf, length) { LOG_DBG("%s (%zu bytes):", name, (size_t)(length)); LOG_HEXDUMP_DBG(buf, length, name); }
#define dtls_debug_dump(name, buf, length) { LOG_DBG("%s (%zu bytes):", name, (size_t)(length)); LOG_HEXDUMP_DBG(buf, length, name); }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name should be wrapped as (name) in 2 places per line, as well as buf and length wrapped in LOG_HEXDUMP()

#define dtls_debug_hexdump(name, buf, length) { LOG_DBG("%s (%zu bytes):", (name), (size_t)(length)); LOG_HEXDUMP_DBG((buf), (length), (name)); }
#define dtls_debug_dump(name, buf, length) { LOG_DBG("%s (%zu bytes):", (name), (size_t)(length)); LOG_HEXDUMP_DBG((buf), (length), (name)); }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to start with applying general rules about macros, that will change much more ;-).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. However, every time we have to change a (#define) line of code, lets correctly fix that line and only correct the other lines that need doing when they need to be updated, so minimizing this particular fix.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree.
The tiny scope if this PR is based on an user request (see #212).
Extending the scope to "general/common rules", which are neither agreed nor discussed before is usually the way the generate quite a lot frustration, because that makes it impossible to improve the project in small steps.

If general rules about the design of macros should be applied to this project, I consider a separate issue for discussion will help much more than to "piggyback" this to tiny PRs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK - what works best for you.

Comment on lines +142 to +143
#define dtls_debug_hexdump(name, buf, length) { if (LOG_DEBUG <= LOG_LEVEL) { LOG_DEBUG("-= %s (%zu bytes) =-\n", name, (size_t)(length)); od_hex_dump(buf, length, 0); }}
#define dtls_debug_dump(name, buf, length) { if (LOG_DEBUG <= LOG_LEVEL) { LOG_DEBUG("%s (%zu bytes):", name, (size_t)(length)); od_hex_dump(buf, length, 0); }}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as previous comment - things need to be wrapped in #define.

fzi-haxel added a commit to fzi-haxel/RIOT that referenced this pull request Jan 12, 2024
Currently upstream in discussion
eclipse/tinydtls#217
fzi-haxel added a commit to fzi-haxel/RIOT that referenced this pull request Jan 15, 2024
Currently upstream in discussion
eclipse/tinydtls#217
fzi-haxel added a commit to fzi-haxel/RIOT that referenced this pull request Jan 15, 2024
Currently upstream in discussion
eclipse/tinydtls#217
@boaks boaks added the available on develop Mark PRs (pre-)available only on develop label Feb 7, 2024
@obgm
Copy link
Contributor

obgm commented Mar 5, 2024

Apart from my general concerns documented in #237 I wonder why this cast is necessary here, anyway: od_hex_dump() also requires a size_t as data_len argument. Therefore, there is no point in passing anything else than size_t.

@boaks
Copy link
Contributor Author

boaks commented Mar 5, 2024

anyway: od_hex_dump() also requires a size_t as data_len argument. Therefore, there is no point in passing anything else than size_t.

You can pass what ever C can convert into a "size_t". Even a uint8_t will work.

@boaks
Copy link
Contributor Author

boaks commented Mar 14, 2024

@obgm
@mrdeep1
@OlegHahm

This PR contains 1 change copied to 4 locations. It is pending now for 3 months.
I'm very deeply frustrated about the process here.
Please come to a conclusion until end of next week.
It's no problem for me to just close it without merging.
But for sure, I don't want to spend more time in it.

@mrdeep1
Copy link
Contributor

mrdeep1 commented Mar 14, 2024

I'm happy for this to be merged as is.

@obgm
Copy link
Contributor

obgm commented Mar 18, 2024

I'm happy for this to be merged as is.

So, then just merge it. I still do not want it but since it is just for debug purposes and most IoT applications usually will not have to deal with numbers that exceed the number space of size_t, I will most likely get over it since many smart people do not seem to have a problem with this PR.

@boaks
Copy link
Contributor Author

boaks commented Mar 18, 2024

I'm sure, if that causes more trouble than it solves, we will simply get new issues.

@boaks boaks merged commit 8c55636 into eclipse:main Mar 18, 2024
6 checks passed
@boaks boaks deleted the fix_size_t_macros branch May 13, 2024 10:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
available on develop Mark PRs (pre-)available only on develop review focus
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants