-
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
Fix reading EXR images on 32bit Windows with OpenEXR 3.3 #1952
base: main
Are you sure you want to change the base?
Fix reading EXR images on 32bit Windows with OpenEXR 3.3 #1952
Conversation
|
Thanks for finding this! Would you mind doing the CLA business noted in the message above? |
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 looks good to me, but I'll defer to @kdt3rd for a second approval.
I reached out to my company's legal department but won't be able to sign off the CLA before monday. |
That's perfectly fine :) Thanks for looking into it. |
hrm, it should have been uintptr_t in the msvc block at lines 35-48, this is more likely my poor win32 programming substituting in for not having stdatomic.h properly implemented in msvc at the time I did it - there should be a 32 vs 64 bit switch depending on what size uintptr_t is - it is always pointing to a uint64 as you note (it's an offset into the file), but might be a 32-bit pointer (4-byte pointer) pointing to that array of 8-byte values, so what is there seems not quite right to me, although will work, but not as efficiently as it could on a 32-bit machine, the implementations of the atomic function for msvc should instead check #if sizeof(void *) == 4 or so and it should deal with uintptr_t... |
concretely, those two functions at line 35 should likely be #ifdef _WIN64 then use Interlocked64 or use Interlocked32 and take uintptr_t values... thanks for finding that |
I'll take a look at implementing it more efficent using the proposed |
I did take another look and tried the following: static inline int
atomic_compare_exchange_strong (
atomic_uintptr_t volatile* object, uintptr_t* expected, uintptr_t desired)
{
uintptr_t prev =
# ifdef _WIN64
(uintptr_t) InterlockedCompareExchange64 (object, desired, *expected);
# else
(uintptr_t) InterlockedCompareExchange ((uintptr_t volatile*) object, desired, *expected);
# endif
if (prev == *expected) return 1;
*expected = prev;
return 0;
} This seemlingly worked on 32 and 64 bits and allowed me to revert However, please note that this code now has a conversion of pointer to a 64-bit value The reason is the define for openexr/src/lib/OpenEXRCore/internal_structs.h Lines 57 to 59 in 8bc3fae
This would introduce a similar error as the one I wanted to fix. However, instead of falsely reading additional bytes, here the four additional bytes wouldn't be correclty overwritten in the scope of I also briefly tried changing the TL;DR I would rather keep the proposed solution to use 64 bit values for |
…pe on 32 bit platforms Signed-off-by: Christopher Schwartz <[email protected]>
be27945
to
6d8e291
Compare
It took a few more days, but now it's signed. |
This PR fixes an issue with
atomic_compare_exchange_strong
in chunk.c due to inappropriate type on 32 bit platforms.Context:
When switching from OpenEXR 3.2.4 to OpenEXR 3.3.2, I noticed that reading image content from file (via
ImfInputFile::readPixels(int, int)
) failed most of the time (but not always) in 32 bit Windows.I found that OpenEXR 3.3 apparently changed the reading routine to use
extract_chunk_table
from OpenEXRCore's chunk.c.Here, two variables
eptr
andnptr
are declared to be of typeuintptr_t
inopenexr/src/lib/OpenEXRCore/chunk.c
Line 568 in 8bc3fae
However, in
openexr/src/lib/OpenEXRCore/chunk.c
Lines 644 to 647 in 8bc3fae
, they are used in an
atomic_compare_exchange_strong
call asuint64_t*
anduint64_t
respectively.See
openexr/src/lib/OpenEXRCore/chunk.c
Lines 38 to 47 in 8bc3fae
While the latter isn't a problem per-se, the usage of
&eptr
asuint64_t*
letsatomic_compare_exchange_strong
interpret whatever is at the address ofeptr
as an 8 byte long number.However, the actual type
uintptr_t
is only 4 bytes long.So, whatever is in the next four bytes will garble the value originally stored in
eptr
.As a result in the comparison of
prev
(which holds "0" if the exchange worked as expected) does not compare to the expected and declare value ofuintptr_t eptr = 0
from line 568 but any number that is in the respective full 8 bytes of memory will fail and set the full 8 bytes to 0.openexr/src/lib/OpenEXRCore/chunk.c
Lines 44 to 46 in 8bc3fae
This not only misleads the calling code to assume that it failed to thread-safe exchange the pointers but also overwrites the
ctable
pointer with the (now 8 bytes of zeros) addresss and eventually (falsely) report "out of memory".openexr/src/lib/OpenEXRCore/chunk.c
Lines 649 to 652 in 8bc3fae
My proposed minimal fix simply changes the type of
eptr
(andnptr
) to always be an unsigned 64 bit value, which should work for both, 32 and 64 bit platforms.