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: 3, Insightful) by crafoo on Saturday December 02 2017, @02:13PM (11 children)

    by crafoo (6639) on Saturday December 02 2017, @02:13PM (#604269)

    I guess my opinion is that they should have just written out each error check test case and been done with it. It would have been more readable and not all that much more typing. Also, if you're going to use a macro for C error handling, maybe consider just using GOTO. Do the custom stuff you need and then jump to the standardized stuff you need instead of wrapping it up in a macro.

    My feeling is that once you've put a conditional or flow-control structure in a macro you've made a mistake.

    I don't have a lot of love for C++ templates either. I guess I've seen them used effectively, although there has always been a competing library that didn't use them and had at least as good performance ... so .. not convinced on the C++ template subject either.

    Starting Score:    1  point
    Moderation   +1  
       Insightful=1, Total=1
    Extra 'Insightful' Modifier   0  
    Karma-Bonus Modifier   +1  

    Total Score:   3  
  • (Score: 0) by Anonymous Coward on Saturday December 02 2017, @08:32PM (4 children)

    by Anonymous Coward on Saturday December 02 2017, @08:32PM (#604385)

    I have been putting a lot of thought into this. For standard C stuff we should be pushing to port constexpr and templates backwards into C from C++. #define is a poor mechanism to define code in. I have been feeling more explicit is better than implicit for awhile now. We are abusing the pre-processor to basically create code and then hide it. There have been a few times where I would have to unwind a define into a real function just to figure out where the subtle bug is. Because none of the existing debuggers let you trace them. This problem has been around a long time and we are abusing the compiler into fixing it. We should be creating structures (hehe) to make it easier for us to do these things instead of abusing current structures. Sure it is 'valid' but over the years #define has become a hell hole of code abuse. All because we have no real other way to do it. I have also seen people abuse #include. I want to hurt them when I see it. It is meaningless to do other than to somehow unpollute your global space.

    Most template slowness was because the compilers were just not really ready for it. They are now. They are doing a very good job at runtime inline and making methods. Whereas the original one just treated it as a super define and inlined everything yet treated it as an atomic unit with no optimization. Which lead to code bloat and poor cache coherency.

    My point is we are already doing basically what templates do, but abusing #define to do it. Why not make the pattern a first class citizen and make our lives easier? We are not going to magically stop doing it.

    • (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].

      • (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.

  • (Score: 3, Insightful) by fyngyrz on Saturday December 02 2017, @10:59PM (5 children)

    by fyngyrz (6567) on Saturday December 02 2017, @10:59PM (#604445) Journal

    C macros are fine. Poor programmers aren't.

    This, as with many other things, strikes me as blaming the language for the weakness of the practitioner. Which, in the case of operations that employ programmers, also means someone's been hiring people for the wrong reasons (like degrees / age / sex / court records / location / health / instead of skill, for example.)

    There's good code and poor code; and macros are code. If people are writing poor macros, then fix your people problem. Either train them more, or stop hiring incompetents.

    If your organization is not keeping track of the quality of code your employees / members are writing and getting blindsided by this sort of thing, why then you have another problem.

    And if it's you that's unable to deal with macros... time for some self-improvement. The macro code capabilities in c aren't broken. Guess what that leaves to fix?

    • (Score: 2) by Pino P on Saturday December 02 2017, @11:40PM (3 children)

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

      stop hiring incompetents

      Easier said than done. Or would you please explain an efficient way to sort out dozens of applications by incompetent candidates to a particular position en masse?

      The macro code capabilities in c aren't broken.

      They're not type-safe. Some people include not type-safe in broken.

      • (Score: 3, Insightful) by fyngyrz on Saturday December 02 2017, @11:59PM (2 children)

        by fyngyrz (6567) on Saturday December 02 2017, @11:59PM (#604470) Journal

        would you please explain an efficient way to sort out dozens of applications by incompetent candidates to a particular position en masse?

        After you've determined that they say they're really good at the language you plan to have them use (demand/specify that info in the job app), then you test them. If you're hiring programmers without testing them, we've found (at least one of) your problems.

        Tests should be progressive; not stupid puzzles, or dips into realms that have to relevance to the job at hand, either. You can get newbies in and out of a test sequence in just minutes; they'll never survive the most simple questions about language details. Better programmers will take longer to check out. But competent testing is an absolute requirement to fill programming positions – if you don't want to be saddled with people who will do damage to the task(s) at hand.

        Also, don't ask for X years of experience in X. Ask for high competence in X. The former doesn't imply the latter, though one might (naively) think so.

        They're not type-safe. Some people include not type-safe in broken.

        Yes, well, you shouldn't hire people with that kind of skills deficit. Problem solved! Anything else I can help you with? :)

        c'mon, smile. Not kidding, though. A fundamental (not basic) programmer skill is managing types. If the language doesn't do it, it's your job (and if the language does do it, there's still no excuse for you to be bad at it.)

        • (Score: 0) by Anonymous Coward on Sunday December 03 2017, @01:09AM

          by Anonymous Coward on Sunday December 03 2017, @01:09AM (#604493)

          What you're suggesting sounds too hard. Wouldn't it be better to just keep turning away introverts, since they fail our shallow personality tests by not being social enough? Wouldn't it be easier to just throw out the applications of anyone who doesn't have a degree? Look how much time we're saving!

          Ugh, why is there a shortage of talented individuals!? We need foreigners to fill the gaps!

        • (Score: 2) by Pino P on Sunday December 03 2017, @03:29AM

          by Pino P (4721) on Sunday December 03 2017, @03:29AM (#604551) Journal

          You can get newbies in and out of a test sequence in just minutes; they'll never survive the most simple questions about language details. Better programmers will take longer to check out.

          It still costs quite a bit of money to administer the tests, especially when it involves flying in candidates from a thousand miles away. This competent testing had better provide improvement in quality that is measurable and substantial, or the shareholders won't stand for it.

          A fundamental (not basic) programmer skill is managing types. If the language doesn't do it, it's your job

          Then why not just write all programs in assembly language and make the programmer do everything?

    • (Score: 2) by DutchUncle on Sunday December 03 2017, @05:22AM

      by DutchUncle (5370) on Sunday December 03 2017, @05:22AM (#604578)

      Actually, C macros sort of suck, compared with IBM assembler macros from the 1970s, or Motorola's macro assembler for the 68000. IBM macros had capabilities close to templates in C++. That said, within the limits of what C macros can do, I agree with your premise that it's not the tool's fault if the programmer uses it badly.