Categories: CodingLinux

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

Reviving an Amiga 600: From Dead Video to a Clean Boot

I managed to score an Amiga 600 motherboard which was faulty for £41. This weekend…

2 weeks ago

The Amiga 1200 That Fought Back: The Faults I Missed the First Time

I recently repaired an Amiga 1200 with a difficult to find fault. Unfortunately, it came…

3 weeks ago

Why Recapping Isn’t Always the Cure: And Amiga 1200 Repair Story

I often see on places such as Facebook that an Amiga owner will show a…

1 month ago

KDE Plasma Automatic Time Zone

I have been a full time KDE Plasma user for quite a while now. Whilst…

1 month ago

The wolfDemo Board Story: From Idea to Reality

I work building open-source cybersecurity solutions for wolfSSL. These solutions often involve embedded environments, which…

2 months ago

Upgrading the RAM Detective: A Firmware Adventure with RAMCHECK

The firmware in my RAMCHECK is very old, there were many updates since then. Unfortunately,…

5 months ago