# simple C program using bind() worked on 9.3 doesn't work on 11.4



## dkl (Aug 27, 2020)

I have distilled my porting issue down to the following C program, which used to work on 9.3 (32-bit) and doesn't work on 11.4 (32-bit) (code below).

The result is bind() giving an error `Address family not supported by protocol family`, or errno 47.

Here's the run of the code on 11.4:

```
@tape$ gcc -o s s.c
@tape$ rm -f /tmp/socketfoo; ./s
failed after bind(): 47: Address family not supported by protocol family
@tape$
```

and here's the output on 9.3:

```
@kiko$ cc -o s s.c
@kiko$ rm -f /tmp/socketfoo; ./s
s=3
@kiko$
```


```
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>

main()
{
    char *local_filename = "/tmp/socketfoo";
    int backlog=5;
    int s;
    int socksize;
    char *saddr;

    if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
    fprintf(stderr, "failed after socket()\n");
    exit(-1);
    }

    socksize = sizeof (short) + strlen(local_filename) + 1;

    saddr = (char *)malloc(socksize);

    *(short *)saddr = AF_UNIX;
    strcpy(saddr+sizeof(short), local_filename);

    if (bind(s, (struct sockaddr *)saddr, socksize) == -1) {
    fprintf(stderr, "failed after bind(): %d: %s\n",
        errno, strerror(errno));
    exit(-1);
    }

    if (listen(s,backlog) != 0) {
      close(s);
      fprintf(stderr, "failed after listen()\n");
      exit(-1);
    }

    fprintf(stderr, "s=%x\n", s);

    exit(0);
}
```


----------



## dkl (Aug 27, 2020)

Btw, we're using GCC 4.8 on the 11.4 system.


----------



## Crivens (Aug 27, 2020)

Do you have a reason to not use the correct structs for the sockaddr? Like, real real reasons?


----------



## olli@ (Aug 27, 2020)

Please read the unix(4) manual page to learn how to use UNIX-domain sockets correctly. You must use a `struct sockaddr_un`. Alternatively, refer to Stevens’ APUE chapter 15.5.2 (advanced interprocess communication, 4.3+BSD).

Also, your `main()` function misses a type declaration (must be `int`), and for the close(2) system call you need to `#include <unistd.h>`.


----------



## mark_j (Aug 27, 2020)

You should also compile with _*-Wall -Werror*_ so the compiler can bash you over the head about your programming issues.


----------



## Deleted member 63539 (Aug 28, 2020)

dkl said:


> Btw, we're using GCC 4.8 on the 11.4 system.


Could you let us know why you still use this outdated compiler? What's the problem with the system default compiler, clang 10?


----------



## ralphbsz (Aug 28, 2020)

It's worse than that. In a sockaddr (..._in) structure, the call expects: (a) the address family, which you did put in there, but perhaps the wrong number of bytes, I don't know whether it the length of a short or an int, (b) the port number, (c) the IP address. Instead, you are giving it an ASCII string, which will be interpreted as binary gibberish. That makes very little sense.

EDIT: Sorry, I'm wrong: For this, you actually want a pathname; for connect, you want port and IP address in a sockaddr_in. My bad.


----------



## mickey (Aug 28, 2020)

ralphbsz said:


> It's worse than that. In a sockaddr (..._in) structure, the call expects: (a) the address family, which you did put in there, but perhaps the wrong number of bytes, I don't know whether it the length of a short or an int, (b) the port number, (c) the IP address. Instead, you are giving it an ASCII string, which will be interpreted as binary gibberish. That makes very little sense.


Because that is how UNIX domain sockets work. The actual socket is created as a special file within the filesystem:

```
/*                                                                                                                  
 * Definitions for UNIX IPC domain.                                                                                 
 */                                                                                                                 
struct sockaddr_un {                                                                                                
        unsigned char   sun_len;        /* sockaddr len including null */                                           
        sa_family_t     sun_family;     /* AF_UNIX */                                                               
        char    sun_path[104];          /* path name (gag) */                                                       
};
```
Although I admit, that the way the OP initializes the data deviates quite a bit from all known standards


----------



## mark_j (Aug 28, 2020)

ralphbsz said:


> It's worse than that. In a sockaddr (..._in) structure, the call expects: (a) the address family, which you did put in there, but perhaps the wrong number of bytes, I don't know whether it the length of a short or an int, (b) the port number, (c) the IP address. Instead, you are giving it an ASCII string, which will be interpreted as binary gibberish. That makes very little sense.



No, this is wrong. What he's done is sort of ok, but his use of just char instead of the structure has lead him to not see the use of sa_len.

eg:
In BSDs it's


```
struct sockaddr {
        unsigned char   sa_len;
        sa_family_t     sa_family;
        char            sa_data[14];
};
```
In Linux (and Solaris):

```
struct sockaddr {
               sa_family_t sa_family;
               char        sa_data[14];
};
```

He might also want to include <sys/un.h> and use the structure as mickey mentions.

Also note the relevant part of https://www.freebsd.org/doc/en_US.I...ers-handbook/sockets-essential-functions.html

If this is the OP's work, he's being too cute. If it isn't, then whomever he's copying it from is a naughty boy. It looks like old-skool coding.


----------



## Crivens (Aug 28, 2020)

Deviates? Is that a fancy name for a bug?


----------



## mark_j (Aug 28, 2020)

Deviates == pioneer surrounded by Indians and 1 bullet left.


----------



## mickey (Aug 28, 2020)

Feels like ages since I last had to use stuff like that, but this compiles and runs on FreeBSD 12.1:

```
#include <stdio.h>                                                                                                  
#include <string.h>                                                                                                 
#include <errno.h>                                                                                                  
#include <unistd.h>                                                                                                 
#include <sys/types.h>                                                                                              
#include <sys/socket.h>                                                                                             
#include <sys/un.h>                                                                                                 
                                                                                                                    
#define MYSOCKET        "/tmp/socketfoo"                                                                            
#define BACKLOG         5                                                                                           
                                                                                                                    
int main(int argc, char *argv[])                                                                                    
{                                                                                                                   
        int s;                                                                                                      
        int rv = -1;                                                                                                
                                                                                                                    
        if((s = socket(PF_UNIX, SOCK_STREAM, 0)) != -1)                                                             
        {                                                                                                           
                struct sockaddr_un sun;                                                                             
                                                                                                                    
                sun.sun_family = AF_UNIX;                                                                           
                strlcpy(sun.sun_path, MYSOCKET, sizeof(sun.sun_path));                                              
                sun.sun_len = SUN_LEN(&sun);                                                                        
                                                                                                                    
                if(!bind(s, (struct sockaddr *)&sun, sun.sun_len))                                                  
                {                                                                                                   
                        if(!(rv = listen(s, BACKLOG)))                                                              
                        {                                                                                           
                                fprintf(stderr, "SUCCESS socket=%x\n", s);                                          
                        }                                                                                           
                        else                                                                                        
                        {                                                                                           
                                fprintf(stderr, "failed to listen (%s)\n",                                          
                                        strerror(errno));                                                           
                        }                                                                                           
                }                                                                                                   
                else                                                                                                
                {                                                                                                   
                        fprintf(stderr, "failed to bind socket (%s)\n",                                             
                                strerror(errno));                                                                   
                }                                                                                                   
                                                                                                                    
                close(s);                                                                                           
                unlink(MYSOCKET);                                                                                   
        }                                                                                                           
        else                                                                                                        
        {                                                                                                           
                fprintf(stderr, "failed to create socket (%s)\n",                                                   
                        strerror(errno));                                                                           
        }                                                                                                           
                                                                                                                    
        return rv;                                                                                                  
}
```


----------



## ekvz (Aug 28, 2020)

mark_j said:


> It looks like old-skool coding.



Agreed. If there was any function with parameters i wouldn't be surprised in the slightest to see the funky old style type definitions but the implicit int return type on main (which technically isn't even used afterwards) is suspicious enough in my opinion. Trying to make assumptions about the actual data types of some system structure while also ignoring any alignment there might be is just the icing on the cake.

I think the code should be rewritten (at this size it's really not to much work) in a more clean way if one wants it to survive the next couple decades. Kinda like what mickey did above.


----------



## a6h (Aug 28, 2020)

Crivens said:


> Deviates? Is that a fancy name for a bug?


My software never has bugs. It just develops random features.


----------



## Crivens (Aug 28, 2020)

ekvz If that was a school, I would welcome a hygienic urban reconstruction program for it.


----------



## dkl (Aug 28, 2020)

You are all right, not using the official structures is bad.  This code is literally decades old and hasn't been touched since it was written.

The real issue is that even if I used the real structures in FreeBSD 9.3, that binary wouldn't work on 11.3, because the "sun_len" slot was added to the beginning of sockaddr_un.  That was unfortunate, as it breaks binary compatibility, no?

As to why we use GCC 4.8... we haven't groomed our C code for clang 10.  Originally I did build with it, but had too many problems.

Also, the C code in our system is minimal.  The rest is common lisp.  The C bits are just for glue between system libraries and CL code.

I appreciate everyone's help on this.  Thanks.


----------



## olli@ (Aug 28, 2020)

dkl said:


> You are all right, not using the official structures is bad.  This code is literally decades old and hasn't been touched since it was written.
> 
> The real issue is that even if I used the real structures in FreeBSD 9.3, that binary wouldn't work on 11.3, because the "sun_len" slot was added to the beginning of sockaddr_un.  That was unfortunate, as it breaks binary compatibility, no?


No, the binary would still work on 11.3 (if it was programmed correctly, of course), because the `COMPAT_FREEBSD*` options in the kernel provide compatibility syscalls for old binaries. These syscalls translate the older structs to newer ones and then call the newer syscalls.


----------



## Crivens (Aug 28, 2020)

dkl said:


> As to why we use GCC 4.8... we haven't groomed our C code for clang 10.  Originally I did build with it, but had too many problems.


I once was in a project where the way to do things (TWTDT) was like that. Once we went -Wall -Werror -Wextra ... and used clangs build-scan plus valgrind, we had 3 months of work to do. After that (and one subcontrcting company fired for incompetence) we suddenly had a pretty stable software. It is well worth the extra mile, believe me.


----------



## ekvz (Aug 28, 2020)

Crivens said:


> I once was in a project where the way to do things (TWTDT) was like that. Once we went -Wall -Werror -Wextra ... and used clangs build-scan plus valgrind, we had 3 months of work to do. After that (and one subcontrcting company fired for incompetence) we suddenly had a pretty stable software. It is well worth the extra mile, believe me.



Absolutely. Teaching people to write clean code is something C is very good at. It simply doesn't forgive laziness and makes you suffer for not paying attention to detail. Everyone gets the drill sooner or later.


----------



## Jose (Aug 28, 2020)

dkl said:


> You are all right, not using the official structures is bad.  This code is literally decades old and hasn't been touched since it was written...
> As to why we use GCC 4.8... we haven't groomed our C code for clang 10.  Originally I did build with it, but had too many problems...


Technical debt incurred.



Crivens said:


> I once was in a project where the way to do things (TWTDT) was like that. Once we went -Wall -Werror -Wextra ... and used clangs build-scan plus valgrind, we had 3 months of work to do. After that (and one subcontrcting company fired for incompetence) we suddenly had a pretty stable software. It is well worth the extra mile, believe me.


Technical debt paid.

Technical debt foreclosed is left as an exercise for the reader.


----------



## shkhln (Aug 28, 2020)

ekvz said:


> Teaching people to write clean code is something C is very good at.



This meme needs to die. C is not a very principled language at all, starting with the "undefined behavior" concept.


----------



## ekvz (Aug 28, 2020)

shkhln said:


> This meme needs to die. C is not a very principled language at all, starting with the "undefined behavior" concept.



I don't see a contradiction there. Quite the opposite. Undefined behavior is just another concept you have to be aware of. Either that or be ignorant and pay the price and that price is what results in writing more and more clean code over time. It reinforces the concept of taking responsibility for your code because the compiler will happily let you do really stupid stuff. It's you who has to think about it and how to not do it.

The only situation i can think of where undefined behavior is semi problematic is strict aliasing which to some extend can be avoided using unions but i know that's often annoying or not really practical. Still when you are aware of the implications it's not much of a problem. It's just when you think you could ignore it that it might bite you.


----------



## shkhln (Aug 28, 2020)

ekvz said:


> Undefined behavior is just another concept you have to be aware of. Either that or be ignorant and pay the price and that price is what results in writing more and more clean code over time. It reinforces the concept of taking responsibility for your code because the compiler will happily let you do really stupid stuff. It's you who has to think about it and how to not do it.



You are implicitly assuming that:

the person originally writing the code is the person dealing with the consequences (this isn't true for OP at least);
the code is broken to an extent that programmers are forced to acknowledge and address the issues.
Undefined behavior can be quite benign until it suddenly isn't. For example, see this case of use after deinitialization: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=248225. Compare this with Rust where destroying/freeing something twice wouldn't be possible (without jumping through the hoops).


----------



## ekvz (Aug 28, 2020)

shkhln said:


> the person originally writing code is the person dealing with the consequences (this isn't true for OP at least).



You have a point there. It only works when the person actually has to maintain the code they wrote but unless they manage to have it work fine for some decades it will come back to haunt them one way or the other. At the size the example consequences would be minor anyways as it's so tiny and complexity is so low the error almost spots itself. Try that with an off by one/overflow/uninitialized variable in a messy 100k loc source of some hard to predict application. Very different outcome.



shkhln said:


> the code is broken to an extent that programmers are forced to acknowledge and address the issues.



Well, it's either broken or it isn't. If the error doesn't have a noticeable effect i'd say there is no problem to begin with.



shkhln said:


> Undefined behavior can be quite benign until it suddenly isn't. For example, see this case of use after deinitialization: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=248225. Compare this with Rust where destroying/freeing something twice wouldn't be possible at all (without jumping through the hoops).



How is some function having a certain set of requirements undefined behavior? At best one could complain that it's only documented as not being thread safe while also calling it multiple times from the same thread is not going to work. The fact that it might work if some obscure condition is met doesn't change that. Besides the way some library is written has nothing to do with the language itself. It either comes down to the function being badly documented or the implementation being faulty.

Edit: I should have read the notes section on the pthread_join documentation. Well, that changes things a bit. It's actually also documented that calling it multiple times from the same thread is not guaranteed to work. It's all on the person who wrote the crashing application then. This is no different from doing a double free and deciding it's fine just because it works in the current environment. I wonder what their logic looks like anyways when it's possible for it to join the threads multiple times. The real problem is probably way bigger.

Regarding to possibility to shoot yourself in the foot with a double free: Well, sure it would be nice if that wasn't possible but it is and therefore has to be taken into account. Also if it wasn't there would have to be some internal magic to prevent it. Now this magic isn't free of charge but there will be cases where you know it isn't needed but the compiler can't deduct it so your final binary contains pointless checks or some other kind of limitation that makes things like that impossible. If you write C you are the one writing the checks, well, or you don't. It's up to you. If you don't like that then some other language (maybe Rust as you suggest?) might be better suited for you.


----------



## shkhln (Aug 28, 2020)

ekvz said:


> How is some function having a certain set of requirements undefined behavior?



Strictly speaking, this an API contract violation. However, since use-after-free as often referred to in that manner*, I don't think this is much of a stretch either. In other words, I'm just being slightly lazy. This doesn't make the issue any less real.

* From the OpenBSD man pages, https://man.openbsd.org/free.3:


> If ptr was previously freed by free() or a reallocation function, the behavior is undefined and the double free is a security concern.






ekvz said:


> If you don't like that then some other language (maybe Rust as you suggest?) might be better suited for you.



Who cares about me? I don't even have source code access for that shit. (Which, wait for it, technically means that all behavior is well defined, since everything is already compiled.)


----------



## ekvz (Aug 28, 2020)

shkhln said:


> Strictly speaking, this an API contract violation. However, since use-after-free as often referred to in that manner*, I don't think this is much of a stretch either. In other words, I'm just being slightly lazy. This doesn't make the issue any less real.
> 
> * From OpenBSD man pages, https://man.openbsd.org/free.3:



Like i said in my edit, after reading the notes section of pthread_join it's pretty clear that the problem is the programmer of the crashing application ignoring the documentation. Undefined behavior is something you don't invoke unless you know some way to make it defined or have a very serious reason to rely on it. He/she/it didn't and it blew up.

The comparison is fine. Actually i would have brought up free myself if you wouldn't have beat me to it. It's just that those kind of problems are very much unavoidable unless you take memory management out of the programmers hands (i think that's what rust does?) or have some more or less clever logic to check the validity of the requested operation. All of that comes with it's own set of drawbacks that depending on the situation might or might not be acceptable.

It's also not even that taking memory management out of the equation would fully eliminate such problems. An api can only reasonably have safety nets for so much usage that does not make any practical sense before it becomes bloated and slow just to safe the world from people who ignore documentation. It's kinda like expecting a second call to strtok to return the same string again even if the documentation says you get the next one just because when you tried your string happened to contain the same token in a row two times.



shkhln said:


> Who cares about me? I don't even have source code access for that shit. (Which technically makes all behavior well defined, since everything is already compiled.)



I am sorry, the "you" in that sentence was meant in a general sense. It was not my intention to refer to you personally. I just wanted to say that if one is writing C it has to accepted that unchecked memory management is part of the package. Sometimes that will be advantageous. Sometimes it will be a waste of time and an unneeded possibility for errors.


----------



## mark_j (Aug 29, 2020)

mickey said:


> Feels like ages since I last had to use stuff like that, but this compiles and runs on FreeBSD 12.1:
> 
> ```
> <snip>
> ...



I know it's syntactically correct, but boy do I hate **argv[]*. ***argv* looks more intuitive.
Just my opinion.


----------



## mark_j (Aug 29, 2020)

ekvz said:


> Like i said in my edit, after reading the notes section of pthread_join it's pretty clear that the problem is the programmer of the crashing application ignoring the documentation. Undefined behavior is something you don't invoke unless you know some way to make it defined or have a very serious reason to rely on it. He/she/it didn't and it blew up.


Good points.

In a software development environment "undefined behavior" is something you plan to avoid. Over the years I've seen many techniques to avoid, for example, double free. In essence, the attempt is to prevent complacency and laziness from slipping into code. Most work real well. That's the beauty of C, you can implement what you want to prevent this potential problem. It is up to you.

A case in point is to look at the OpenVMS systems code. The use of descriptors to control access to functions takes more work but protects the system.

Some of the mish-mash code and programming I've seen has boggled me. If people want to write C then learn the language and UNDERSTAND IT.
If it's too hard for you (in a general sense), then please, program in a more controlled language.

Finally, mistakes are made, but good code auditing will find them.


----------



## mickey (Aug 29, 2020)

mark_j said:


> I know it's syntactically correct, but boy do I hate **argv[]*. ***argv* looks more intuitive.


That's open for interpretation...


----------



## a6h (Aug 29, 2020)

mark_j said:


> I know it's syntactically correct, but boy do I hate **argv[]*. ***argv* looks more intuitive.


I prefer **argv over *argv[] and both are exist in K&R.
P.S. I know pointer and array are not same creature!


----------



## Crivens (Aug 29, 2020)

In fact, this is legal in C:

```
int i; char* c; char x;
....

x=i[c];
```


----------



## a6h (Aug 29, 2020)

Crivens said:


> x=i[c];




```
x=c[i];
```



Crivens said:


> In fact, this is legal in C:




```
c++; // if c is a pointer => it's OK
c++; // if c is an array ==> it's not OK
```


----------



## memreflect (Aug 29, 2020)

mark_j said:


> boy do I hate **argv[]*. ***argv* looks more intuitive.


If you think that's bad, you'd love the book Modern C (there's a link to a 20-chapter free PDF version).  While technically correct, the conventions it uses still look strange to me:

```
int main(int argc, char* argv[argc+1])

char* strcpy(char dst[restrict static 1], char const src[restrict static 1])
```
If a function parameter is declared with the form `T v[static 1]`, pass `(void*)0` as that parameter in a call to that function and observe how Clang's -Wnonnull flag works for just that one case and not 0, 0UL, `(int*)0`, etc.  I'm not saying it's a useless flag, but considering it only catches one specific case…

The standard NULL macro (with no single standard value) is discouraged as well.  Apparently, some systems exist where NULL is defined to be a zero-valued integer literal of type T (e.g. 0, 0UL) and `sizeof(T) < sizeof(void*)`, which can result in program crashes when passing NULL to variadic functions that expect pointers like printf(3) with certain format specifications and execl(3).

I also get the feeling that the author would have preferred this declaration of fgets(3) instead:

```
char* fgets(size_t n, char buf[restrict n], FILE* restrict stream);
```

C's syntax is confusing to a lot of people, and I'm honestly not sure if the conventions in the book make it better or worse.


----------



## a6h (Aug 29, 2020)

memreflect said:


> C's syntax is confusing to a lot of people, and I'm honestly not sure if the conventions in the book make it better or worse.


I'm not familiar with books published by Manning Publications. Thanks for quoting from that book, now I understand why their books cover are so creepy: "it's consistent with their materials". Beside K&R, I think the "Expert C" by "Peter von der Linden" is the best and only necessary book for C programming.


----------



## mark_j (Aug 30, 2020)

memreflect said:


> If you think that's bad, you'd love the book Modern C (there's a link to a 20-chapter free PDF version).  While technically correct, the conventions it uses still look strange to me:
> 
> ```
> int main(int argc, char* argv[argc+1])
> ...



I'm wary of using* [static]* etc nowadays. Let's face it compilers are pretty damn good at optimising. I mean, years ago we used to use *register *for every variable in a loop, nowadays, the compiler will ignore it and do what it wants (and do it better in most instances)

There may be times when you want to specify this stuff, in embedded or real time, for example, but for the average coding Joe, they're a nuance best left in the standard's boffins minds. (tautology)


----------

