Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

In curl 7.88.1, wrapping curl_easy_setopt in macro is broken when compiling with GCC <12.1 #10726

Closed
kchow-FTNT opened this issue Mar 10, 2023 · 7 comments
Labels

Comments

@kchow-FTNT
Copy link

kchow-FTNT commented Mar 10, 2023

I did this

Our project is currently using GCC 10.3 and as far as I know, this should be supported by curl 7.88.1. However, upon trying to upgrade curl from 7.86.0 to 7.88.1 we get some compilation errors in our existing code. The issue is known and appears to be fixed in GCC 12.1; #pragma statements in macros can be reordered and this interferes with disabling -Wdeprecated-declarations.

This is a rough simplification of what our code looks like, and a highly simplified version of the current logic within curl 7.88:

typedef enum {
    CURLOPT_DEPRECATED __attribute__((__deprecated__)),
    CURLOPT_OKAY,
} CURLoption;

int curl_easy_setopt(CURLoption val);

#define CURL_IGNORE_DEPRECATION(statement) \
  _Pragma("GCC diagnostic push") \
  _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") \
  statement \
  _Pragma("GCC diagnostic pop")

static void __attribute__((__warning__("bad"))) __attribute__((__unused__)) __attribute__((__noinline__)) curl_warn(void) {
                __asm__("");
}

#define curl_easy_setopt(_expr) \
  __extension__ ({ \
    CURLoption _internal_opt = (_expr); \
    if (__builtin_constant_p(_internal_opt)) { \
        CURL_IGNORE_DEPRECATION( \
            if (_internal_opt == CURLOPT_DEPRECATED) { \
                curl_warn(); \
            } \
        ) \
    } \
    curl_easy_setopt(_internal_opt);\
  })

#define SPRINGBOARD4(_expr) \
    ({ \
        int ret = (_expr); \
        ret; \
    })

#define SPRINTBOARD3(_expr) \
    ({ \
        if (SPRINGBOARD4(_expr) < 0) { \
            return -1; \
        } \
    })

#define SPRINGBOARD2(_expr) \
  SPRINTBOARD3(_expr)

#define SPRINGBOARD1(_expr) \
  SPRINGBOARD4(_expr)

int test(void) {
    SPRINGBOARD2(curl_easy_setopt(CURLOPT_OKAY));
    if (SPRINGBOARD1(curl_easy_setopt(CURLOPT_OKAY)) < 0) {
        return -1;
    }
    return 0;
}

Please try this code snippet out on godbolt.org under GCC < 12.1 The compiler output should look something like this:

<source>: In function 'test':
<source>:51:5: warning: 'CURLOPT_DEPRECATED' is deprecated [-Wdeprecated-declarations]
   51 |     SPRINGBOARD2(curl_easy_setopt(CURLOPT_OKAY));
      |     ^~~~~~~~~~~~
<source>:2:5: note: declared here
    2 |     CURLOPT_DEPRECATED __attribute__((__deprecated__)),
      |     ^~~~~~~~~~~~~~~~~~
<source>:52:1: error: expected expression before '#pragma'
   52 |     if (SPRINGBOARD1(curl_easy_setopt(CURLOPT_OKAY)) < 0) {
      | ^  
Compiler returned: 1

And the preprocessor output should look something like this:

int test(void) {
   
#pragma GCC diagnostic push
   
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
   
#pragma GCC diagnostic pop
    do { if (({ int ret = (__extension__ ({ CURLoption _internal_opt = (CURLOPT_OKAY); if (__builtin_constant_p(_internal_opt)) { if (_internal_opt == CURLOPT_DEPRECATED) { curl_warn(); } } curl_easy_setopt(_internal_opt); })); ret; }) < 0) { return -1; } } while (0);
    if (
#pragma GCC diagnostic push
   
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
   
#pragma GCC diagnostic pop
   ({ int ret = (__extension__ ({ CURLoption _internal_opt = (CURLOPT_OKAY); if (__builtin_constant_p(_internal_opt)) { if (_internal_opt == CURLOPT_DEPRECATED) { curl_warn(); } } curl_easy_setopt(_internal_opt); })); ret; }) < 0) {
        return -1;
    }
    return 0;
}

Compilation will fail on every macro wrapping curl_easy_setopt. Take a look at the preprocessor output for both to see the reason why; In GCC < 12.1 the #pragma directives are placed in odd positions and depending on how the macro is structured we either get a deprecation warning on the unused deprecated CURLOPT, or the code gets completely mangled and we get the weird expected expression before '#pragma' error.

If you try this same code block with >=12.1 the #pragmas are correctly placed and compilation is fine.

I expected the following

libcurl macros need to be rewritten to handle this case for GCC < 12.1

curl/libcurl version

7.88.1

operating system

Proprietary platform, however this is a compilation issue so that shouldn't matter.

@bagder bagder added the build label Mar 10, 2023
@bagder
Copy link
Member

bagder commented Mar 10, 2023

I'm not following.

A bazillion people have built with 7.88.1 headers on gcc < 12.1 already. What is it in your use of it that makes the setup break?

@bagder
Copy link
Member

bagder commented Mar 10, 2023

/cc @monnerat

@monnerat
Copy link
Contributor

I tried your code with gcc 9.3 and I confirm.
This is a gcc bug that is triggered only because the curl_easy_setopt() call is a macro argument. Obviously gcc messes up the pragma/code order in a macro expansion.

Possible caller's workarounds using the current curl code base are:

  • Use a temp variable: CURLcode ret = curl_easy_setopt(...); SPRINGBOARD2(ret);
  • Replace caller's macros by static inline functions.
  • Disable deprecation for the whole module (not officially supported): -DCURL_DISABLE_DEPRECATION in gcc command line.
  • Disable curl_easy_setopt argument type checking: for the whole module (not officially supported): -DCURL_DISABLE_TYPECHECK in gcc command line.
  • Disable arg type check for the problematic statements with parentheses: SPRINGBOARD2((curl_easy_setopt)(CURLOPT_OKAY));

On curl's side, I don's see any possible workaround. We'll probably need to raise the conditioning gcc version again :-( This is a pity: gcc 12.1 is only 10 month old.

@kchow-FTNT
Copy link
Author

Thanks for your prompt response, I'll forward to the team that there are no plans for curl to handle the GCC bug. I too quickly tried a few tricks to try to get the #pragma directives to be correctly placed and I couldn't get it working.

Looks like pushing for GCC update is the best solution.

All the best.

@bagder
Copy link
Member

bagder commented Mar 13, 2023

@monnerat: are you able to get a fix that raises the bar to 12.1 to get this fixed in the next release?

bagder added a commit that referenced this issue Mar 17, 2023
Reported-by: kchow-FTNT on github
Fixes #10726
@bagder
Copy link
Member

bagder commented Mar 17, 2023

@kchow-FTNT does #10784 fix the issue for you?

@bagder bagder closed this as completed in c3f3c25 Mar 17, 2023
@kchow-FTNT
Copy link
Author

LGTM!

bch pushed a commit to bch/curl that referenced this issue Jul 19, 2023
Reported-by: kchow-FTNT on github
Fixes curl#10726
Closes curl#10784
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging a pull request may close this issue.

3 participants