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

scrotGrabMousePointer: error out on failure #317

Merged
merged 2 commits into from
May 30, 2023

Conversation

N-R-K
Copy link
Collaborator

@N-R-K N-R-K commented May 25, 2023

This turns the previous warnings into errors instead - since we couldn't accomplish what the user asked us to.

And while working on this, I also realized that the in-place mutation wouldn't violate strict aliasing and is legal. So remove the failure point created by malloc entirely.

ref: #272

N-R-K added 2 commits May 25, 2023 20:00
initially this path wasn't chosen because we suspected this might
violate C's "strict-aliasing" rule. but a more careful look shows that
this is not the case.

xcim->pixels (due to being dynamically allocated memory) does not have
any declared type, and so it's effective type is determined by the last
write, in our case `unsigned long`.

	pixels[i] = xcim->pixels[i];

in all iteration of the loop, `xcim->pixels[i]` at the time of the read,
has the effective type of `unsigned long` which is what we are reading
it as, so there's no issue.

the `pixels[i]` write changes the written-to region's effective type to
`uint32_t` but that value is never read until the `imlib_create_image_using_data`
call later, which reads it as `uint32_t` - matching it's effective type.

as such, there's no real strict-aliasing violation here and we can
simply fixup the array in place. this reduces a potential runtime error
and (in theory) uses less memory.

ref: https://port70.net/~nsz/c/c99/n1256.html#6.5p6
ref: resurrecting-open-source-projects#193 (comment)
if the user asks us to grab the pointer, we should either do that
otherwise fail rather than continuing on.

also remove a redundant comment, it should be clear that a function
named XFixesGetCursorImage is indeed returning back a cursor image.

ref: resurrecting-open-source-projects#272
@daltomi
Copy link
Collaborator

daltomi commented May 25, 2023

Let's try not to place commits that do not go with what is indicated by the PR (I understand, I also did it sometimes)

src/scrot.c Show resolved Hide resolved
Comment on lines +458 to +464
uint32_t *pixels = (uint32_t *)xcim->pixels;
/* XFixesCursorImage returns pixels as `unsigned long`, which is typically
* 64bits, but imlib2 expects 32bit packed integers. */
for (size_t i = 0; i < pixcnt; i++)
pixels[i] = xcim->pixels[i];
if (sizeof *xcim->pixels > sizeof *pixels) {
for (size_t i = 0; i < pixcnt; ++i)
pixels[i] = xcim->pixels[i];
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This does violate C's aliasing rules: https://www.iso-9899.info/n1570.html#6.5p7

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, I've read that and explained in the commit why there's no violation. If you think otherwise then feel free to explain in more detail.

Copy link
Collaborator Author

@N-R-K N-R-K May 26, 2023

Choose a reason for hiding this comment

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

To elaborate, each element of an array is an object by itself (C99 § 3.14). The write into u32[i] only changes the effective type of that region, not the entire array, the rest of the elements still retain their effective type because they haven't been written to, C99 § 6.5:

If a value is stored into an object having no declared type through an lvalue having a type that is not a character type, then the type of the lvalue becomes the effective type of the object for that access and for subsequent accesses that do not modify the stored value.

Copy link
Contributor

@guijan guijan May 27, 2023

Choose a reason for hiding this comment

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

It's perfectly valid as far as C goes that xcim->pixels is aligned to unsigned long, but not uint32_t. There is no requirement that uint32_t's alignment requirements have to be <= unsigned long's, or that unsigned long's alignment requirements have to be an integer multiple of uint32_t's.

Perhaps we can let it slip, Eric S. Raymond's struct packing guide says:

I’ve learned something rather reassuring from working with the source code for the reference implementation of NTP. It does packet analysis by reading packets off the wire directly into memory that the rest of the code sees as a struct, relying on the assumption of minimal self-aligned padding - or zero padding in odd cases like 690x0.

The interesting news is that NTP has apparently being getting away with this for decades across a very wide span of hardware, operating systems, and compilers, including not just Unixes but under Windows variants as well. This suggests that platforms with padding rules other than self-alignment are either nonexistent or confined to such specialized niches that they’re never either NTP servers or clients.

i.e. in all modern machines _Alignof(type) == sizeof(type), although I know that long double can be an exception on i386 with its 80-bit FPU: it has sizeof(long double) == 10 and _Alignof(long double) == 16 in the SysV ABI.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, no unnecessary casts as in line 458 please.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Also, no unnecessary casts as in line 458 please.

The cast wasn't there on my first iteration, but it caused a compiler warning. So I added it.

There is no requirement that uint32_t's alignment requirements have to be <= unsigned long's, or that unsigned long's alignment requirements have to be an integer multiple of uint32_t's.

Okay, that's fair. I was only thinking of aliasing and didn't consider alignment. But if such a machine exists, I kinda doubt Imlib2 will work there, because IIRC I saw some of the Imlib2 loaders treat raw memory as struct as well - which would most likely be broken under such machines.

Copy link
Contributor

@guijan guijan May 27, 2023

Choose a reason for hiding this comment

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

it caused a compiler warning

Ugh, compilers forcing us to write C++-like C.

Edit: actually, I guess it's not the compiler's fault if we write known UB and it warns, the unnecessary cast is the "I know what I'm doing" idiom of C.

@N-R-K N-R-K merged commit e0589e4 into resurrecting-open-source-projects:master May 30, 2023
@N-R-K N-R-K deleted the inplace_fixup branch May 30, 2023 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants