0

I found myself in need of computing the maximal value of 2 operands.

  • The 2 operands may have side effects and must be evaluated only once, and
  • The evaluation take place in preprocessing macro, and allocation of additional variable is not preferred (although if viable, it's allowed).

The best solution I've come up so far is to bit-or them together, which has smaller overhead than arithmetic addition. Even though this doesn't get the maximal value of the 2 operands, it tells a value that's big enough to hold working context of either size.

typedef struct {
    void (*initfunc)(void *restrict x);
    void (*updatefunc)(void *restrict x, const void *restrict in, size_t len);
    void (*finalfunc)(void *restrict x, void *restrict out);
} hash_funcs_set;

typedef struct {
    unsigned            hlen_msg;
    unsigned            hlen_mgf;
    hash_funcs_set_t    hfuncs_msg, hfuncs_mgf;
} pkcs1_padding_oracles_base_t;

#define PKCS1_PADDING_ORACLES_T(...)            \
    struct {                                    \
        pkcs1_padding_oracles_base_t base;      \
        uint8_t hashctx[__VA_ARGS__];           \
    }

typedef PKCS1_PADDING_ORACLES_T() pkcs1_padding_oracles_t;

#define PKCS1_PADDING_ORACLES_CTX_SIZE_X(hmsg, hmgf) (  \
        sizeof(pkcs1_padding_oracles_base_t) +          \
        (CTX_BYTES(hmsg) | CTX_BYTES(hmgf)) )

// CTX_BYTES is a function-like macro that invokes hmsg (which
// can be either another function-like expression macro, or a 
// function pointer), to obtain the size of hash function working 
// context size.
// Possible definitions of a hmsg/hmgf:
#define macro_SHA256(q) (\
        q==1 ? 32 /* output bytes */ : \
        q==2 ? 256 /* size of working variable set */ : \
        q==3 ? (intptr_t)SHA256_Init : \
        q==4 ? (intptr_t)SHA256_Update : \
        q==5 ? (intptr_t)SHA256_Final : \
        0)

intptr_t info_SHA256(int q) { return macro_SHA256(q); }
...
#define OUT_BYTES(obj) (obj(1))
#define CTX_BYTES(obj) (obj(2))
...

The reason I deem this acceptable is because I'm actually allocating memory working contexts for a data structure union (therefore integers) which may be used by 2 different set of functions at different times without conflict, and I hope some XY problem can be spotted and better solution can be devised.

I expect PKCS1_PADDING_ORACLES_CTX_SIZE_X(hmsg,hmgf) expands to constant expression when hmsg and hmgf are constant expressions, but they can be anything and may cause side effects.

The language is standard C, any version. Compiler-specific features are not preferred.

DannyNiu
  • 1,313
  • 8
  • 27
  • "*The best solution I've come up*". There are a few unclear points in the question. Please show that attempted solution to make the question clearer. – kaylum Aug 31 '21 at 03:54
  • @kaylum I've included a code fragment to demonstrate how I intend to calculate working context size. – DannyNiu Aug 31 '21 at 03:58
  • 1
    `CTX_BYTES(hmsg) | CTX_BYTES(hmgf)` How does that get the max? For example, if one operand is 4 and the other is 8 the result will be 12. – kaylum Aug 31 '21 at 04:01
  • @kaylum edited the paragraph just preceeding the code fragment. – DannyNiu Aug 31 '21 at 04:02
  • 2
    I do not understand. `The 2 operands have side effects` Ergo they can't be calculated in preprocessor, they are not constant. – KamilCuk Aug 31 '21 at 05:53
  • @KamilCuk Strictly speaking, they're calculated in the expression expanded from the preprocessing macro definition. – DannyNiu Aug 31 '21 at 05:56
  • 1
    Please try to simplify your example more, and extend it to a [example], even if it has side-effects. I don't get the goal you try to reach. – the busybee Aug 31 '21 at 05:57
  • "when hsmg and hmgf are constant expressions, but they can be anything and may cause side effects." This does not really go together well for me. – Gerhardh Aug 31 '21 at 10:28
  • Is this a practical problem, or a puzzle you have been set, or a puzzle you have set yourself? – Steve Summit Aug 31 '21 at 11:44
  • @SteveSummit One I set myself. I've already had the partial solution of using bitwise-or, I'm just not sure if anybody has a better way. – DannyNiu Aug 31 '21 at 11:54

1 Answers1

1

The 2 operands have side effects

That means they are not constant expressions. So just write a function to calculate the max. With proper type.

// hash_ret_type - the return type of hash function.
// Or it's just unsigned long long or uintmax_t, if super lazy.

hash_ret_type hash_max(hash_ret_type a, hash_ret_type b) {
     return a > b ? a : b;
}

#define PKCS1_PADDING_ORACLES_CTX_SIZE_X(hmsg, hmgf) (  \
        sizeof(pkcs1_padding_oracles_base_t) +          \
        hash_max(CTX_BYTES(hmsg), CTX_BYTES(hmgf)) )

If you are interested in using GNU extensions, you might want to interest in max() linux kernel macros.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111