This is an archive of the discontinued LLVM Phabricator instance.

[LLD] Imply "longjmp" in `/guard:cf`
ClosedPublic

Authored by pengfei on Aug 29 2022, 6:09 PM.

Details

Summary

This is MSVC's behaviour. LLD was matching it before D99078. Let's go back this way.

Diff Detail

Event Timeline

pengfei created this revision.Aug 29 2022, 6:09 PM
Herald added a project: Restricted Project. · View Herald TranscriptAug 29 2022, 6:09 PM
pengfei requested review of this revision.Aug 29 2022, 6:09 PM
Herald added a project: Restricted Project. · View Herald TranscriptAug 29 2022, 6:09 PM
alvinhochun added inline comments.Aug 30 2022, 3:09 AM
lld/COFF/DriverUtils.cpp
109–110

Since ehcont implies cf, perhaps in this case it should also imply longjmp?

pengfei added inline comments.Aug 30 2022, 4:40 AM
lld/COFF/DriverUtils.cpp
109–110

No idea. But if we make ehcont imply longjmp, an option like -guard:cf,nolongjmp,ehcont won't disable nolongjmp. So it should align with MSVC too. Could you help to have a check? Thanks!

rnk added inline comments.Aug 30 2022, 10:54 AM
lld/COFF/DriverUtils.cpp
109–110

I think to align with MSVC we'd have to change the code structure to process negation options after positive options, rather than updating the feature mask in order. We would also need to check the semantics of /guard:longjump,nolonjump / /guard:nolongump,longjump, those probably become warnings or errors.

Attention to detail like this is appreciated, but I'd also approve this change as is to get us back to where we were.

pengfei added inline comments.Aug 30 2022, 6:08 PM
lld/COFF/DriverUtils.cpp
109–110

Yeah. Option consistency is important, however, an explicit or implicit confliction option is in the grey area.
AFAIK, Clang driver always overrides former by latter without warning or error. Does MSVC take precedence on negation options?
I'm not a frequent user of MSVC. So I'm not sure if MSVC is constant in such "UB" options or if we are overengineering on mocking it.
I'd like to be conservative here, i.e., not let ehcont imply longjmp, given I don't have environment to verify it.

I did a non-exhaustive test with clang++ ehcont_guard_msvc.cpp -o ehcont_guard_msvc.exe -target x86_64-pc-windows-msvc -Xclang -ehcontguard -O -Xlinker /guard:xxxxxx, using MSVC Incremental Linker Version 14.29.30146.0, then checked using llvm-readobj --coff-load-config ehcont_guard_msvc.exe.

The result is a bit unexpected:

link flagguard fidguard longjmpguard ehcont
(no /guard)nonono
/guard:nononono
/guard:cfyesyesno
/guard:no,cfyesyesno
/guard:longjmpyesnono
/guard:cf,longjmpyesyesno
/guard:cf,nolongjmpyesnono
/guard:nolongjmp,cfyesyesno
/guard:ehcontyesnoyes
/guard:cf,ehcontyesyesyes
/guard;cf,nolongjmp,ehcontyesnoyes
/guard:cf,longjmp,ehcontyesyesyes
/guard:longjmp,ehcontyesnoyes
/guard:cf,longjmp,ehcont,nononono
/guard:no,cf,longjmp,ehcontyesyesyes
  • Options are parsed in order, negation does not override later options.
  • longjmp does not actually enable guard long jump (might be a bug?)
  • ehguard does indeed not enable guard long jump.

This is the code I used:

#include <cstdio>
#include <setjmp.h>

struct my_exception_t {
    int code;
};

__attribute__((noinline))
void do_test(bool should_throw) {
    printf("Starting test, should_throw = %d\n", should_throw);
    try {
        if (should_throw) {
            throw my_exception_t { 1234 };
        }
        puts("Exception not thrown.");
    } catch (my_exception_t ex) {
        printf("Caught exception with value %d.\n", ex.code);
    }
}

int main(int argc, char *argv[]) {
    if (argc > 1) {
        do_test(true);
    } else {
        do_test(false);
    }
    jmp_buf my_jmp_buf;
    jmp_buf my_jmp_buf2;
    if (setjmp(my_jmp_buf) > 0) {
        puts("Longjmp target reached.");
        return 0;
    }
    puts("Performing longjmp...");
    longjmp(my_jmp_buf2, 1);
    puts("Unexpectedly returned from longjmp!");
    return 0;
}

Great job! Thanks @alvinhochun !

If we consider the longjmp not enabling long jump is a bug, and fix the table accordingly, then it is exactly what LLD is doing with this patch. It looks to me MSVC handles the options in order too. Anyway, it matches my understanding in options parsing.

Great job! Thanks @alvinhochun !

If we consider the longjmp not enabling long jump is a bug, and fix the table accordingly, then it is exactly what LLD is doing with this patch. It looks to me MSVC handles the options in order too. Anyway, it matches my understanding in options parsing.

@alvinhochun Does this patch seem ok to you in this form, or do you think it would make sense to match link.exe's behaviour wrt longjmp more exactly?

Great job! Thanks @alvinhochun !

If we consider the longjmp not enabling long jump is a bug, and fix the table accordingly, then it is exactly what LLD is doing with this patch. It looks to me MSVC handles the options in order too. Anyway, it matches my understanding in options parsing.

@alvinhochun Does this patch seem ok to you in this form, or do you think it would make sense to match link.exe's behaviour wrt longjmp more exactly?

I think this patch in its current form is probably fine but honestly I don't know. It would be nice to have a second opinion from someone who actually use the MSVC toolchain.

rnk accepted this revision.Sep 8 2022, 11:52 AM

I mentioned earlier I think this is fine as is, so I'll go ahead and mark it so in Phab.

This revision is now accepted and ready to land.Sep 8 2022, 11:52 AM
This revision was automatically updated to reflect the committed changes.

Thanks all for review!