Coding

POSIX File Handling and Undefined Behaviour

Whilst porting code between Linux and macOS I have come across two issues which make assumptions about how something works but in reality different implementations of libc handle them differently. In this post I’ll talk about recent issues I faced with fopen() and fclose() in codebases.

fopen()

In the code I was porting from Linux to macOS there was a section which opened a file in “a+” mode, read the contents and then as time went on appended more data to the file. Everything was going great, but in macOS the file wasn’t being read at all. The reason is undefined behaviour for the read pointer as outlined in the Linux man page for open:

POSIX is silent on what the
initial read position is when using this mode. For glibc, the
initial file position for reading is at the beginning of the
file, but for Android/BSD/MacOS, the initial file position for
reading is at the end of the file.

http://man7.org/linux/man-pages/man3/fopen.3.html

The Linux code was assuming that when the file was opened in “a+” mode the read pointer was always at the start of the file. But in macOS the read pointer is right at the end of the file when opened. Fixing this was as simple as a basic seek but it was difficult to find this behaviour difference documented outside of the Linux man pages.

fclose()

This issue comes from porting code the other way around, from macOS to Linux. The code in question did this:

fclose(fileptr);

Which is fine, but it was possible that this code could be executed when there is no file open and fileptr is NULL. In macOS this causes fclose() to just return with no operation executed. But in Linux fclose(NULL) will crash with a segmentation fault.

I couldn’t find any explicit documentation on this, but many instances of other people hitting this crash. POSIX doesn’t define what should happen if a NULL pointer is passed to fclose() so this becomes undefined behaviour. Again this was easy to fix with a simple ‘if’ statement.

The moral of the story

The key takeaway here is “undefined behaviour” may not always be explicitly defined in documentation. If there is no explicit documentation for a given input to or usage of a function it is likely that different operating systems will implement it in different ways.

LinuxJedi

Recent Posts

Amiga 4000 With Lots of Little Problems

I’ve had a few people send me things in to repair lately. Amongst these was…

4 weeks ago

Amiga A3640 CPU Card Repair

Lately, I've been very busy, but haven't had many interesting things to blog about happen.…

2 months ago

Two special Amiga 4000s: Finishing Jools Repair

You may remember that the A4000 known as "Jools" went to its new owner for…

3 months ago

Repair of a Trident VLB Graphics Card

Hot on the heels of repairing the Cirrus Logic VLB card, I acquired a dead…

4 months ago

Raspberry Pi JTAG Programming – 2025 Edition

It seems that every time I document how to do Xilinx JTAG programming from a…

4 months ago

Repair of a Cirrus Logic VLB Graphics Card

Fellow vintage computer collector, Gammitin, challenged me to fix four VLB graphics cards, as well…

4 months ago