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

Avoid overflows in left shifts #545

Merged
merged 1 commit into from
Oct 1, 2019
Merged

Conversation

ilammy
Copy link
Collaborator

@ilammy ilammy commented Sep 30, 2019

Left-shifting uint8_t types kinda works because of integer promotion rules. However, this is incorrect on platforms with 16-bit int types. We should explicitly use 32-bit unsigned integers to get the correct behavior on all platforms (and avoid warnings by undefined behavior sanitizer).

Left-shifting uint8_t types kinda works because of integer promotion
rules. However, this is incorrect on platforms with 16-bit int types.
We should explicitly use 32-bit unsigned integers to get the correct
behavior on all platforms (and avoid warnings by undefined behavior
sanitizer).
@ilammy ilammy added the core Themis Core written in C, its packages label Sep 30, 2019
@Lagovas
Copy link
Collaborator

Lagovas commented Sep 30, 2019

I don't understand why we should use 32 bit variables for 8bit results? we use explicitly defined 32 bit variable which stores 32 bits and then we place each byte to separate 1 byte variable. where is here undefined behavior? Anyway, we need here 32 bits to store crc32. And we don't tell that we support 16-bits platforms, so it's not our problem that old platform can't work with *32_t types.
@vixentael what do you think?

@ilammy
Copy link
Collaborator Author

ilammy commented Sep 30, 2019

@Lagovas, the issue is with this expression:

result = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3);

Regardless of the result type, if byteN values are smaller than int then they are first extended to int (or unsigned int if they are unsigned), then all the shifts and bitwise operations are performed, and only then the result is extended or truncated to the result type. (These are know as integer promotion rules in the standard.)

This means that if byteN are uint8_t and unsigned int is 16-bit then the compiler is free to interpret the shifts as unsigned left shifts of 16-bit values, making << 16 and << 24 technically undefined behavior as these are shifting more that the width of the left operand.

Obviously, it works as intended even with uint8_t on x86 and ARM because of the way left shifts are implemented there,* but strictly speaking this may cause undefined behavior and sanitizers do warn about that.

* It’s not like that. After reading through Intel’s manual it seems that if the shifts are** interpreted as 16-bit on x86 then the byte values will be really lost and the carry flag will contain some undefined value. ARM is the same if it has 16-bit registers.

** gcc and clang do not generate 16-bit code anymore, so again we’re ‘safe’.

Switching the types to uint32_t is the cleanest way to avoid this issue. Keeping the variables uint8_t and inserting explicit casts into that expression will work too, but that’s less readable and produces the same machine code.

@Lagovas
Copy link
Collaborator

Lagovas commented Sep 30, 2019

but our left operand has enough size to store result value. result defined as 32 bit variable, not 16.

@ilammy
Copy link
Collaborator Author

ilammy commented Sep 30, 2019

The type of the result is not important here. Even if it were bool the computation should be first performed with extended integer values and only then converted to the result type. It’s the types of the operands that matter. If they are uint32_t then on platforms with 32-bit int types no promotions are performed, and on 16-bit platforms the operations are performed with 32-bit values (with whatever tricks the compiler uses for that).

@vixentael
Copy link
Contributor

we don't support 16-bit platforms, we haven't tried even testing on 'em

@ilammy ilammy merged commit da6066a into cossacklabs:master Oct 1, 2019
@ilammy ilammy deleted the fix-shifts branch October 1, 2019 14:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core Themis Core written in C, its packages
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants