0

I have been working with pjsip for a while now, and in an effort to get it to work for UWP, I have encountered a seemingly fatal flaw in the base C code. I am getting errors 'addrs': unknown size and 'deprecatedAddrs': unknown size in the code below.

PJ_DEF(pj_status_t) pj_enum_ip_interface2( const pj_enum_ip_option *opt,
                       unsigned *p_cnt,
                       pj_sockaddr ifs[]) {
    pj_enum_ip_option opt_;

    if (opt)
    opt_ = *opt;
    else
    pj_enum_ip_option_default(&opt_);

    if (opt_.af != pj_AF_INET() && opt_.omit_deprecated_ipv6) {
    pj_sockaddr addrs[*p_cnt];
    pj_sockaddr deprecatedAddrs[*p_cnt];
    unsigned deprecatedCount = *p_cnt;
    unsigned cnt = 0;
    int i;
    pj_status_t status;
...

The error comes for the two lines pj_sockaddr addrs[*p_cnt]; and pj_sockaddr deprecatedAddrs[*p_cnt]; . I feel that I understand that this should not work, due to the fact that C cannot have dynamic arrays and needs to preallocate space. However, pjsip is a well-established library used rather often, so my question is: Is there a situation in which this would function at all?

klutt
  • 30,332
  • 17
  • 55
  • 95
Scornz
  • 380
  • 3
  • 14

3 Answers3

2

This is valid c99 syntax. It's called a variable-length array. You might want to add the -std=c99 flag to your compiler.

EDIT: Given the comments below, here's an example on how to use dynamic memory to replace VLAs.

PJ_DEF(pj_status_t) pj_enum_ip_interface2( const pj_enum_ip_option *opt,
                       unsigned *p_cnt,
                       pj_sockaddr ifs[]) {
    pj_enum_ip_option opt_;

    if (opt)
    opt_ = *opt;
    else
    pj_enum_ip_option_default(&opt_);

    if (opt_.af != pj_AF_INET() && opt_.omit_deprecated_ipv6) {
    
    /* Dynamically allocate memory to replace VLAs
     * Use calloc if you need it initialized to zero
     */ 
    pj_sockaddr *addrs = malloc((*p_cnt) * sizeof (pj_sockaddr));
    pj_sockaddr *deprecatedAddrs = malloc((*p_cnt) * sizeof (pj_sockaddr));
    
    unsigned deprecatedCount = *p_cnt;
    unsigned cnt = 0;
    int i;
    pj_status_t status;

    ...


    /* Remember that memory allocated from the heap needs to be freed */
    free (addrs);
    free (deprecatedAddrs);
    
    /* Not needed, but a good practice */
    addrs = NULL; 
    deprecatedAddrs = NULL;

    ...
    
    return status;
Michael Gruner
  • 487
  • 4
  • 7
1

If you're using gcc or clang, then the flag std=c99 or something like that.

VLA:s are from C99, but were made optional in C11.

If you're using MSVC, then you're out of luck. It does not support C99. There's a question about that here: Does Visual Studio 2017 fully support C99?

If you want to change the code to something equivalent, then you could use alloca. So change

pj_sockaddr addrs[*p_cnt]; 

to

pj_sockaddr *addrs = alloca(sizeof *addrs * *p_cnt);

That is basically what's going on under the hood of VLA:s anyway, so it's a quick fix. The only difference will be if you're using the sizeof operator later on in the code, so if there is a loop somewhere that looks like this:

for(int i=0; i<sizeof addrs; i++) {
    // Code
}

Then you would have to do something about that too.

You can use malloc instead of alloca but that might impact performance and you need to free the memory afterwards. Usually, I prefer using malloc but since you're not writing this from scratch then it does not really matter. After all, since the original code is using VLA:s, you're not adding any problems.

In short, the problems with VLA:s (and alloca) is that you cannot error check the allocation, and if that you risk blowing up the stack. I wrote an answer about that here: https://stackoverflow.com/a/58163652/6699433

klutt
  • 30,332
  • 17
  • 55
  • 95
0

The MSVC compiler does not support every feature from the C99 standard, including variable-length arrays. You can make this code compatible with MSVC by changing the following lines:

pj_sockaddr addrs[*p_cnt];
pj_sockaddr deprecatedAddrs[*p_cnt];

To use malloc:

pj_sockaddr *addrs = malloc(*p_cnt * sizeof(pj_sockaddr));
pj_sockaddr *deprecatedAddrs = malloc(*p_cnt * sizeof(pj_sockaddr));
Joshua Yonathan
  • 490
  • 1
  • 5
  • 16