Allegro breaks waitpid
Chris Katko

I've got a program (based on some Stack Overflow posts) that reads a Linux program's memory by processing /proc/%PID%/maps and then uses ptrace and waitpid to freeze a program (required), read its memory, and release it.

It works fine, until I call Allegro init and now it doesn't work. It just hangs on waitpid(). Any idea why?

Source attached. Target process PID is hardcoded at the top of the source file. To switch to "use allegro" vs "works without allegro" just scroll to bottom
in main() and set "int working" to false and it'll use Allegro.

Link simply with:

g++ reader.cpp -o reader -lallegro

[edit] OH, I just remembered. On modern kernels you have to set an environment key so you can read a program's memory.

echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope

And 1 to restore it. (I doubt it's permanent)

from here:

https://askubuntu.com/questions/41629/after-upgrade-gdb-wont-attach-to-process

[edit]

Also attached is my test program that simply has some simple common data, and prints its own PID. Simply run it, and then put the PID into the source code of reader.cpp. (Yeah, I could, and did, have argc/argv but I removed it for reasons.) It compiles with simple-as-possible: g++ test.cpp -o test

[edit] I just checked with getpid() and getppid() (parent PID) before and after Allegro is initialized and it doesn't appear to change it.

There ARE options with waitpid for groupip, and other criterea to wait for:

- https://linux.die.net/man/2/waitpid

But I honestly don't understand them. I tried all three/four modes of -2, -1, 0, and 1 and none of them worked. (0 being the original mode).

If I pass WNOHANG, it... won't hang. But it'll still never successfully pickup data either. Which works fine without Allegro.

[edit] I found the problem Allegro's simple test example code (and/or library calls)... are setting global errno to 11! And errno should be "reset to zero" before calls to ptrace!

The question is... why is Allegro 5 throwing errors?! The second attached reader.cpp should be the newer version with errno printf checks resulting in the following output:

[run_allegro_and_game] errno was [0]   start of function
[run_allegro_and_game] errno was [11] after al_init  <---
[run_allegro_and_game] errno was [11] al_create_timer (didn't change it?)
[run_allegro_and_game] errno was [0] al_create_display (reset it to zero?)
[run_allegro_and_game] errno was [0] al_create_event_queue
[run_allegro_and_game] errno was [0] al_register_event_source(s)
[run_allegro_and_game] errno was [11]  al_flip_display(); <----

Elias

errno gets set by most libc functions - so for example if Allegro checks if configuration files exist and they don't - libc would set it to a "does not exist" error, I would assume?

Edgar Reynaldo

Excuse me please if I'm being delusional, but can't you set a hardware breakpoint on errno and watch its value change?

And also tell gdb to set it to zero and continue every time the breakpoint is hit? That would allow you to run your test normally perhaps.

EDIT
So Chris, any progress?

Should allegro be resetting errno before libc calls?

GullRaDriel

You should reset errno before any function setting it and that you want to check after.
At some point in the past it wasn't thread safe. AFAIK it's now a thread local variable so it should be more reliable than before.

That said, you only check errno in case the function returns an error, or that the function documentation specifically said otherwise. Some functions sets errno to strange values while they perfectly work.

Edit:

My common errno check is as following (from my memory):

errno=0;
if( my_func() != 0 ) /* adjust to func return code */
{
    int error = errno ; /* save errno because logging functions can change it */
    log( LOG_ERR , "func failed at line %d of %s: %s (%d)" , __LINE__ , __FILE__ , strerror( error ), error ); /* also display the value in case strerror cannot find something usefull */
}

Chris Katko

So Chris, any progress?

Well, I know the problem. So I reset errno before I use it now.

Quote:

Should allegro be resetting errno before libc calls?

That's up to you C guys. Looks like it's standard issue for users to reset it before use, however, it should be very clear in the documentation when an Allegro function is capable of affecting a global (or TLS) variable under "side effects".

Because it's not like you're going to see "errno" directly modified by Allegro until you run into this problem. So you could easily fall into the trap of assuming you're the FIRST user of errno so it shouldn't have been touched yet.

Thread #617756. Printed from Allegro.cc