Stories
Slash Boxes
Comments

SoylentNews is people

posted by martyb on Saturday December 02 2017, @01:48PM   Printer-friendly
from the see-what-we-did-there? dept.

https://www.cossacklabs.com/blog/macros-in-crypto-c-code.html

Like death and taxes, one thing that you can be sure of is that using C macros in a modern software project will cause a debate. While for some macros remain a convenient and efficient way of achieving particular programming goals, for others they are opaque, introduce the unnecessary risk of coding errors, and reduce readability.

The criticism of macros is particularly acute in the wider security community. Among Cossack Labs' engineers and the core Themis crypto library contributors there are people who previously worked on auditing cryptographic implementations of critical code. Their typical knee-jerk reaction to macros was always "kill it with fire and never use it again". Taking no sides, we would like to assess both pros and cons of using such dangerous things as macros in security code (as we faced the issue when developing Themis) and suggest some techniques for lowering the accompanying risks.

We'll also discuss a custom "for-audit" build target for Themis designed specifically to generate source code that exposes the macros to inspection because we appreciate the need for security software to be subject to detailed source code scrutiny.


Original Submission

 
This discussion has been archived. No new comments can be posted.
Display Options Threshold/Breakthrough Mark All as Read Mark All as Unread
The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
  • (Score: 2) by Pino P on Saturday December 02 2017, @11:36PM (3 children)

    by Pino P (4721) on Saturday December 02 2017, @11:36PM (#604462) Journal

    Once you backport templates to C, why not just use those features of C++? Is it to target platforms such as 6502-based retro computers that barely have a C89 compiler, let alone a compiler for modern C++?

    And even in C++, you still can't automatically pass __FILE__ and __LINE__ at the place of instantiation (as opposed to the place of definition) without either A. a macro or B. explicitly repeating __FILE__ and __LINE__ in every expression that might require them. Option B violates the Don't Repeat Yourself principle [wikipedia.org].

    Starting Score:    1  point
    Karma-Bonus Modifier   +1  

    Total Score:   2  
  • (Score: 2) by arcz on Sunday December 03 2017, @03:14PM (2 children)

    by arcz (4501) on Sunday December 03 2017, @03:14PM (#604675) Journal

    __FILE__ and __LINE__, are, imo, two of the only useful macros. My favorite macro is one I made called AT. I just do:

    std::cerr << AT << "Something happened." << std::endl;

    And that turns into "./bla.c:99:void foo::bar(int): Something happened."

    Good macros:

    • Do thing like this, using __FILE__ and __LINE__
    • Add code/remove code based on compiler or CPU architecture.

    Bad macros:

    • Do ANYTHING that could be done without them, including by switching from C to C++ and using templates.


    #ifdef __clang__
    #define CLANG_OPT_BARRIER std::atomic_signal_fence(std::memory_order_seq_cst)
    #else
    #define CLANG_OPT_BARRIER
    #endif

    The above code* would allow me to inject optimization barriers in clang but not other compilers. (clang's somewhat poor optimization passes on occassion make this needed due to optimizer "bugs" [well, techincally the code still works, but it might be 10x slower in some cases than without this trick])
    *Not actually tested, as I usually just add #ifdef __clang__ directly into my source. But IIRC this is basically what it would do.

    I also love macros for doing this:

    static inline size_t index1_pv(size_t n) __attribute__((always_inline))
        {
    #ifdef __x86_64__
            if (rpnx_unlikely(n==0)) return 0;
            else
                {
                    size_t i;
                    asm("bsrq %1,%0\n" : "=r"(i) : "r"(n));
                    return i;
                }
    #else
            int i = 0; while ((1 << i)

    This one is a real example of my code (in my monoque repository). This is a great use of macros! I have a fast instruction for intel platforms and a generic one for others. So this code runs VERY fast on intel x86_64! (basically two instructions) vs the nested loop used on others.

    • (Score: 2) by Pino P on Sunday December 03 2017, @05:28PM (1 child)

      by Pino P (4721) on Sunday December 03 2017, @05:28PM (#604721) Journal

      Instead of using macros for the last case, you could use separate files:

      // index1_pv_amd64.c
      static inline size_t index1_pv(size_t n) __attribute__((always_inline))
          {
              if (rpnx_unlikely(n==0)) return 0;
              else
                  {
                      size_t i;
                      asm("bsrq %1,%0\n" : "=r"(i) : "r"(n));
                      return i;
                  }

      // index1_pv_pure.c
      static inline size_t index1_pv(size_t n) __attribute__((always_inline))
          {
              int i = 0; while ((1 << i)

      Then the x86-64 target would compile and link the _amd64 source files, while other targets would use the pure C file.

      • (Score: 2) by arcz on Saturday December 09 2017, @05:03PM

        by arcz (4501) on Saturday December 09 2017, @05:03PM (#607716) Journal

        One of the few things I hate more than macros are complex build systems.
        Also that code needed to be inlinable so it had to go in a header. Platform specific headers would be a nightmare.