-
Notifications
You must be signed in to change notification settings - Fork 626
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
Invalidenum workaround #859
Invalidenum workaround #859
Conversation
Signed-off-by: Peter Hillman <[email protected]>
src/bin/exrenvmap/readInputImage.cpp
Outdated
else if (hasEnvmap (in.header())) | ||
{ | ||
// validate type is known before using | ||
const Envmap* typeInFile = &envmap (in.header()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be easier to read the intention here if written as:
const Envmap& typeInFile = envmap (in.header()
const int envMapAsInt = * reinterpret_cast<const int*>(&typeInFile);
since envmap() returns const&. The use of pointers to integers is confusing, and at least this limits that to a single reinterpret_cast line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The underlying issue is this -
IMF_STD_ATTRIBUTE_IMP (envmap, Envmap, Envmap)
expands to
const TypedAttribute<type> & \
IMF_NAME_ATTRIBUTE(name) (const Header &header) \
{ \
return header.typedAttribute <TypedAttribute <type> > \
(IMF_STRING (name)); \
}
which would be a TypedAttribute, where Envmap is
enum Envmap
{
ENVMAP_LATLONG = 0, // Latitude-longitude environment map
ENVMAP_CUBE = 1, // Cube map
NUM_ENVMAPTYPES // Number of different environment map types
};
TypedAttribute has this:
const T & value () const;
so shouldn't the following work? It eliminates all the casting, and lets the compiler do the work in a bullet proof way.
if (ENVMAP_LATLONG == envmap (in.header()).value();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't see the originating bug report, but this avoids setting a value, so I think it would avoid the issue as described in the PR comments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an interesting one. I have many questions :)
src/bin/exrenvmap/readInputImage.cpp
Outdated
else if (hasEnvmap (in.header())) | ||
{ | ||
// validate type is known before using | ||
const Envmap* typeInFile = &envmap (in.header()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The underlying issue is this -
IMF_STD_ATTRIBUTE_IMP (envmap, Envmap, Envmap)
expands to
const TypedAttribute<type> & \
IMF_NAME_ATTRIBUTE(name) (const Header &header) \
{ \
return header.typedAttribute <TypedAttribute <type> > \
(IMF_STRING (name)); \
}
which would be a TypedAttribute, where Envmap is
enum Envmap
{
ENVMAP_LATLONG = 0, // Latitude-longitude environment map
ENVMAP_CUBE = 1, // Cube map
NUM_ENVMAPTYPES // Number of different environment map types
};
TypedAttribute has this:
const T & value () const;
so shouldn't the following work? It eliminates all the casting, and lets the compiler do the work in a bullet proof way.
if (ENVMAP_LATLONG == envmap (in.header()).value();
src/bin/exrenvmap/readInputImage.cpp
Outdated
else if (hasEnvmap (in.header())) | ||
{ | ||
// validate type is known before using | ||
const Envmap* typeInFile = &envmap (in.header()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't see the originating bug report, but this avoids setting a value, so I think it would avoid the issue as described in the PR comments?
src/bin/exrheader/main.cpp
Outdated
{ | ||
switch (e) | ||
int envmapAsInt = *( reinterpret_cast<const int*>(&e) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the hard core definition of default that default means "any known legal value" and is UB for unknown values? If that's the case, should we instead be declaring Envmap as an int?
i.e.
enum Envmap : int { ...
Would declaring it as such resolve the problem without all the casting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changing the definition of Envmap
does seem to resolve the problem. Is doing that an ABI change? I was trying to avoid that but maybe the simplicity of the code makes it worth doing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be possible to do an nm on the .so with and without the : int? If you grep the output for Envmap, we should be able to see if the mangled names with Envmap have changed or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The mangled names do not change, but they move around slightly, which is a little concerning.
Will all architectures guarantee that the Envmap enum was of type int
before this change? I'd understood that some compilers might use an 8 bit type if everything could fit in and no type was specified. If that's the case, it might cause issues in user code even if the library itself was identical
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's inherently a problem with any sort of reinterpret_cast based solution, but I think if it was an issue it would have raised its head by now.
If the names do not change, but move around, that's not going to cause linking problems. I'm in favor of moving to an explicit type to avoid compilers having opinions about types :)
This is an issue with all attributes of type enum, since values read from
files need to be preserved, you can't discard invalid values. If we're
going to change Envmap to an int, for consistency we should change the
others. But enums exist for a reason, it would be a shame to forego the
construct just because the sanitizer complains about it. The sanitizer
complaint seems bogus, there's no way that can amount to a legitimate
vulnerability, right? Would it be better to keep the code as is and
disable the sanitizer warnings in the fuzz test?
…On Wed, Dec 2, 2020 at 4:40 PM Nick Porcino ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In src/bin/exrheader/main.cpp
<#859 (comment)>
:
> {
- switch (e)
+ int envmapAsInt = *( reinterpret_cast<const int*>(&e) );
Would it be possible to do an nm on the .so with and without the : int? If
you grep the output for Envmap, we should be able to see if the mangled
names with Envmap have changed or not.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#859 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AFC3DGKXYK2CWKFH6L5LENTSS3M7DANCNFSM4TOTY3TQ>
.
--
Cary Phillips | R&D Supervisor | ILM | San Francisco
|
I believe compilers might optimize switch statements assuming enum variables only have valid values, so you might end up with an arbitrary case statement executing when the value is not valid. Code might also not be expecting no branch to be taken, which could cause issues. I think the only enums of concern are
Despite the potential ABI change, I like that @meshula's solution also bypasses any sanitizer warnings in user code and keeps things cleaner. If more enums are added in the future, it might pay to have a more abstract class to handle them rather than just a plain enum, so unknown values can be handled properly and detected cleanly |
Could we get around this by using a union between an enum and int?
…On Wed, Dec 2, 2020 at 5:58 PM peterhillman ***@***.***> wrote:
I believe compilers might optimize switch statements assuming enum
variables only have valid values, so you might end up with an arbitrary
case statement executing when the value is not valid. Code might also not
be expecting *no* branch to be taken, which could cause issues.
I think the only enums of concern are Envmap and DeepImageState. All the
other enums are validated when reading, and again by the sanityCheck
function. That's because Compression and the enums in TileDescription are
used to compute the number of chunks within the file, so if they aren't a
currently known value then the file layout cannot be determined.
PixelType and LineOrder are also sanityChecked when a file is opened, so
unknown values cannot be preserved (the *InputFile constructors throw an
exception). That's not strictly necessary. Pixels cannot be read from an
image which has an unknown PixelType but exrstdattr could theoretically
still copy the data from input to output, and might be possible to read
other parts of a multipart file.
Despite the potential ABI change, I like that @meshula
<https://github.com/meshula>'s solution also bypasses any sanitizer
warnings in user code and keeps things cleaner.
If more enums are added in the future, it might pay to have a more
abstract class to handle them rather than just a plain enum, so unknown
values can be handled properly and detected cleanly
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#859 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AFC3DGLL6OH2KIF7GWP65ZLSS3WEXANCNFSM4TOTY3TQ>
.
--
Cary Phillips | R&D Supervisor | ILM | San Francisco
|
Signed-off-by: Peter Hillman <[email protected]>
Thinking about Peter's earlier note that he's heard of compilers picking an arbitrary size for an enum (and I've heard that as well) If it's possible that an enum can be other than 32 bits, then a union of int and enum wouldn't allow strict comparison, due to endianness. This shows the basic problem with reinterpret casting as well. key: hi = high byte, mh = middle hi, ml = middle lo, lo = low byte memory arrangement:
|
Revisiting this, have we settled on @meshula's "enum Envmap : int" solution? @peterhillman, would you like to rework the PR around that? |
Signed-off-by: Peter Hillman <[email protected]>
This now uses "enum : int" for attributes which may be extended with future states. The sanitizer warning suppressions for Envmap and DeepImageState attributes are no longer needed so those have been removed too |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm :)
Address https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=26641
exrenvmap checks for unknown values of EnvMap attribute and avoids setting an EnvMap
enum
to an unknown value is undefined behavior.exrheader
also has simliarly workarounds for EnvMap attributes.exrcheck
also confirms that any EnvMap and DeepImageState attributes are set to valid states