C Why does defining a "_close()" function causes fclose() segmentation faults?

I was able to port a program originally written for Linux. After a lot of learning, trial, and error, I replaced all the Linux specific stuff with FreeBSD counterparts. I proceeded to cobble together a Makefile that I'm sure most people would criticize me heavily for. It compiled and linked. Then of course when I went to run it I get a segmentation fault.

Mind you, while I do have a background in C and programming in general, I am by no means an "experienced" C programmer, let a lone a systems programmer. Most of what I know about C, FreeBSD user land programming, Sockets, and IO Multiplexing, is stuff I learned by watching some video courses. To what surmounts to a period of a few weeks. Specifically for the purpose of porting Linux software to FreeBSD.

Anyway...

After some debugging (if that's what you would call sprinkling around sprintf() statements), I track it down to the fclose() system call. I'm confused. Online research points the finger at memory corruption, and the tool to use is devel/valgrind. I've heard about it before, but never had a reason to actually sit down and use it. So I take this opertunity to use it.

It spits this out:

Code:
==2629== Invalid read of size 4
==2629==    at 0x2BF8C0: _close (in /usr/me/daniel/Documents/SeaTidePool/bin/ckpool)
==2629==    by 0x4AA2C37: ??? (in /lib/libc.so.7)
==2629==    by 0x4AA2D4C: fclose (in /lib/libc.so.7)
==2629==    by 0x2CA244: json_load_file (load.c:1045)
==2629==    by 0x2299E7: parse_config (ckpool.c:1442)
==2629==    by 0x228419: main (ckpool.c:1747)
==2629==  Address 0x4 is not stack'd, malloc'd or (recently) free'd
==2629==
==2629==
==2629== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==2629==  Access not within mapped region at address 0x4
==2629==    at 0x2BF8C0: _close (in /usr/home/me/Documents/SeaTidePool/bin/ckpool)
==2629==    by 0x4AA2C37: ??? (in /lib/libc.so.7)
==2629==    by 0x4AA2D4C: fclose (in /lib/libc.so.7)
==2629==    by 0x2CA244: json_load_file (load.c:1045)
==2629==    by 0x2299E7: parse_config (ckpool.c:1442)
==2629==    by 0x228419: main (ckpool.c:1747)

Pretty much giving me information I already knew. Except one thing that I find peculiar:

Code:
at 0x2BF8C0: _close (in /usr/home/me/Documents/SeaTidePool/bin/ckpool)

I am unable to track down where that call is happening. At this point I'm pushing the boundaries of my knowledge. I can only make educated guesses or just brute force things. Is a macro involved?

I search the source code files for the string "_close", and something comes up in a header file:

C:
void _close(int *fd, const char *file, const char *func, const int line);
#define _Close(FD) _close(FD, __FILE__, __func__, __LINE__)
#define Close(FD) _close(&FD, __FILE__, __func__, __LINE__)

And the implimentation in a C source file:

C:
void _close(int *fd, const char *file, const char *func, const int line)
{
    int sockd;

    if (*fd < 0)
        return;
    sockd = *fd;
    LOGDEBUG("Closing file handle %d", sockd);
    *fd = -1;
    if (unlikely(close(sockd))) {
        LOGWARNING("Close of fd %d failed with errno %d:%s from %s %s:%d",
               sockd, errno, strerror(errno), file, func, line);
    }
}

I have no clue what purpose this serves, but it does seem suspicious. I suspect this is overriding something somewhere. I rename the function by appending a '1' to it. Everything compiles, and I run the program. It doesn't crash with a segmentation fault.

But why? What did I do, and how was this even working on Linux to begin with?
 
In this case instead of valgrind you should use lldb or gdb to get a backtrace.

You can either start the program inside gdb or pick up the corefile that is probably left on your disk.
gdb `which myprogram` myprogram.core
 
Thankfully I know a thing or two about Valgrind.

Code:
==2629== Invalid read of size 4
==2629==    at 0x2BF8C0: _close (in /usr/me/daniel/Documents/SeaTidePool/bin/ckpool)
==2629==    by 0x4AA2C37: ??? (in /lib/libc.so.7)
==2629==    by 0x4AA2D4C: fclose (in /lib/libc.so.7)
==2629==    by 0x2CA244: json_load_file (load.c:1045)
==2629==    by 0x2299E7: parse_config (ckpool.c:1442)
==2629==    by 0x228419: main (ckpool.c:1747)
==2629==  Address 0x4 is not stack'd, malloc'd or (recently) free'd

This is saying that there is a call to 'fclose' that is calling 'close' with an invalid pointer. The last line says that the address is 0x4.

You say you suspect that your "_close" is overriding something somewhere. Strictly speaking is overloading, no overriding. (Overriding is a mechanism that existists in languages with class inheritance and virtual functions like Java and C++).

The function that you are replacing is the syscall wrapper '_close'. The interface for that is defined here https://github.com/freebsd/freebsd-src/blob/main/sys/kern/syscalls.master (the libc interface is the same, though that isn't always the case). As you can see the function signature is different.

Using the name "_close" should be ringing very loud alarm bells. I would systematicaly reject that in any code review. All external names that begin with an underscore are reserved. FreeBSD libc can use it as it is a system library. Your code should not use it, and by using it you are invoking Undefined Behaviour. You can sometimes get away with UB, but not this time. See https://en.cppreference.com/w/c/language/identifier.

I you were using C++ then it would still be UB but the name mangling would prevent the collision so you would get away with it. You could also use namespaces to avoid collisions.

The solution is easy
  1. don't use leading underscores
  2. don't use reserved names (hmm I'm not sure if POSIX reserves names like "close")
 
Last edited:
And why did this work on Linux? Looking at libc.so on Rocky 8.9 amd64 I see

Code:
0000000000120420 t __GI___close
0000000000120420 T __close
0000000000120420 t __libc_close
0000000000120420 W close

The name of the external function is '__close' (with two underscores). 'close' is a weak alias for __close. I imagine the two other names are used internally, they are marked as local.

So the reason is that Linux uses two leading underscores whilst FreeBSD uses just one.
 
There is an old convention about leading _ in symbols.
One _ means this is private to the runtime.
Two _ means more private, like compiler dependent.
Three is absolute "here be dragons".

We once had a __main and a _main in the language runtime which made it possible to skip out the argument parsing, as the definitions were like _main(char* commandline) and __main(void) where even the code to obtain the command line was missing.

Your _close is likely replacing a _close from the libc which is needed for *BSD but the glibc, as usual, has it's own ideas.
Rename that. And stay away from leading _ in symbol names, this is not portable and you could easily find yourself juggeling hand grenades while looking at that spare ring on the floor in front of you.
 
Looks like *fd is passed in as the file descriptor, not a pointer to the file descriptor.
Yikes, if I could contact the original author I should ask him about this. Seems like this could be a potential memory leak (and maybe memory corruption).

This is saying that there is a call to 'fclose' that is calling 'close' with an invalid pointer. The last line says that the address is 0x4.
Thanks, that helps me understand what's going on.

So the reason is that Linux uses two leading underscores whilst FreeBSD uses just one.
Ah, okay. This is good, I was afraid I had changed some core functionality of the program. Looks like I didn't.
 
Ah, okay. This is good, I was afraid I had changed some core functionality of the program. Looks like I didn't.
Wrong. On Linux it does not clash, in FreeBSD it looks like it does. Please change that (maybe x_close) and try again.
 
Wrong. On Linux it does not clash, in FreeBSD it looks like it does. Please change that (maybe x_close) and try again.
I had changed it to _close1 to remove the clash and get it to work, but yes, I should change that to something that doesn't start with an underscore.
 
Back
Top