Macro argument bounds checking?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Jim Cook

    Macro argument bounds checking?

    We have a macro which takes various index constants as an argument and
    offsets into an array. The macro can be an Lvalue or Rvalue. The index
    is not zero based. I would like a compile time error displayed if the
    index is out of bounds. Is there a way to do this well?

    I read through the FAQ that I could find mentioned, and did not see this
    sort of question in the preprocessor section anywhere.

    What I came up with is below. It does in fact generate errors using my
    16-bit and 32-bit compilers, but it feels like I'm relying a little bit
    on the behavior of the MS compiler and would like to know a portable
    proper C method.

    Thank you for any time spent on this.


    The code and run-time clip are below. The valid index range is from 20
    to 27 inclusive. Any other value should produce an error. Testing for
    non-integer is not required.


    // #define argument range testing
    #include "limits.h"

    volatile unsigned short usArray[8];
    volatile unsigned short usI;
    volatile unsigned char ucArray[8];
    volatile unsigned char ucI;

    #define usAccess(r) \
    (usArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])
    #define ucAccess(r) \
    (ucArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])

    void main(void)
    {
    usAccess(19) = 19; /* bad */
    usAccess(20) = 20;
    usAccess(27) = 27;
    usAccess(28) = 28; /* bad */
    usI = usAccess(19); /* bad */
    usI = usAccess(20);
    usI = usAccess(27);
    usI = usAccess(28); /* bad */
    ucAccess(19) = 19; /* bad */
    ucAccess(20) = 20;
    ucAccess(27) = 27;
    ucAccess(28) = 28; /* bad */
    ucI = ucAccess(19); /* bad */
    ucI = ucAccess(20);
    ucI = ucAccess(27);
    ucI = ucAccess(28); /* bad */
    }
    #if 0
    ------------------ Output ------------------
    C:\TEMP\>cl /c /W3 test.c
    Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
    Copyright (C) Microsoft Corp 1984-1998. All rights reserved.

    test.c
    test.c(16) : warning C4307: '*' : integral constant overflow
    test.c(19) : warning C4307: '*' : integral constant overflow
    test.c(20) : warning C4307: '*' : integral constant overflow
    test.c(23) : warning C4307: '*' : integral constant overflow
    test.c(24) : warning C4307: '*' : integral constant overflow
    test.c(27) : warning C4307: '*' : integral constant overflow
    test.c(28) : warning C4307: '*' : integral constant overflow
    test.c(31) : warning C4307: '*' : integral constant overflow
    #endif

  • Ivan Vecerina

    #2
    Re: Macro argument bounds checking?

    "Jim Cook" <jcook@strobeda ta.com> wrote in message
    news:bk880s$k42 @dispatch.conce ntric.net...
    | We have a macro which takes various index constants as an argument and
    | offsets into an array. The macro can be an Lvalue or Rvalue. The index
    | is not zero based. I would like a compile time error displayed if the
    | index is out of bounds. Is there a way to do this well?
    ....
    | volatile unsigned short usArray[8];
    | volatile unsigned short usI;
    ....
    | #define usAccess(r) \
    | (usArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])

    Here is something that will fail more reliably, and should
    work I think (though it's at the limits of the standard):

    volatile unsigned short usArray[8];

    #define usAccess(r) \
    ( *((unsigned short(*)[20<=r&&r<=27])usArray)[r-20] )

    int main()
    {
    usAccess(20) = 20;
    usAccess(21) = 21;
    usAccess(27) = 27;
    usAccess(19) = 19; /* bad */
    usAccess(28) = 28; /* bad */
    }

    The idea is that the conditional expression is used
    to define the size of an array ( 0 or 1 ).
    The following type expression defines a pointer
    to that array:
    (unsigned short(*)[20<=r&&r<=27])

    A pointer of that type is then used to perform offset
    calculations. And this will fail/be illegal if the array
    size is zero (some compilers may even complain before).

    A key feature of this approach is that it will fail
    to compile if the parameter of usAccess is not a compile-
    time constant (while the macro you posted would just
    lead to undefined behavior...).


    This is really ugly though...
    (I'd rather use some C++-based technique than this...)


    Regards,
    --
    Ivan Vecerina - expert in medical devices, software - info, links, contact information, code snippets



    Comment

    • The Real OS/2 Guy

      #3
      Re: Macro argument bounds checking?

      On Tue, 16 Sep 2003 23:57:16 UTC, Jim Cook <jcook@strobeda ta.com>
      wrote:
      [color=blue]
      > We have a macro which takes various index constants as an argument and
      > offsets into an array. The macro can be an Lvalue or Rvalue. The index
      > is not zero based. I would like a compile time error displayed if the
      > index is out of bounds. Is there a way to do this well?
      >
      > I read through the FAQ that I could find mentioned, and did not see this
      > sort of question in the preprocessor section anywhere.
      >
      > What I came up with is below. It does in fact generate errors using my
      > 16-bit and 32-bit compilers, but it feels like I'm relying a little bit
      > on the behavior of the MS compiler and would like to know a portable
      > proper C method.
      >
      > Thank you for any time spent on this.
      >
      >
      > The code and run-time clip are below. The valid index range is from 20
      > to 27 inclusive. Any other value should produce an error. Testing for
      > non-integer is not required.
      >
      >
      > // #define argument range testing
      > #include "limits.h"
      >
      > volatile unsigned short usArray[8];
      > volatile unsigned short usI;
      > volatile unsigned char ucArray[8];
      > volatile unsigned char ucI;
      >
      > #define usAccess(r) \
      > (usArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])
      > #define ucAccess(r) \
      > (ucArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])[/color]

      Both macros are wrong!
      Please name a number that can be in r that is both, <= 20 AND >= 27 as
      required to get TRUE from the ?: operator.
      The numbers 21 trough 26 in r will fail on && because the left hand
      side of && will tell FALSE and the right hand side will never been
      tested.
      The number 20 (like all lower numbers) in r will deliver 0 because it
      is truly lower than 27.
      The numer 27 (like all numbers greater 20) will fail because it is >
      than 20.

      May be you means || but even than your limits are wrong.
      [color=blue]
      > ------------------ Output ------------------
      > C:\TEMP\>cl /c /W3 test.c
      > Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
      > Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
      >
      > test.c
      > test.c(16) : warning C4307: '*' : integral constant overflow
      > test.c(19) : warning C4307: '*' : integral constant overflow
      > test.c(20) : warning C4307: '*' : integral constant overflow
      > test.c(23) : warning C4307: '*' : integral constant overflow
      > test.c(24) : warning C4307: '*' : integral constant overflow
      > test.c(27) : warning C4307: '*' : integral constant overflow
      > test.c(28) : warning C4307: '*' : integral constant overflow
      > test.c(31) : warning C4307: '*' : integral constant overflow
      > #endif
      >[/color]
      2*INT_MAX is integral constant overflow!

      --
      Tschau/Bye
      Herbert

      eComStation 1.1 Deutsch Beta ist verügbar

      Comment

      • Irrwahn Grausewitz

        #4
        Re: Macro argument bounds checking?

        "The Real OS/2 Guy" <os2guy@pc-rosenau.de> wrote:
        [color=blue]
        >On Tue, 16 Sep 2003 23:57:16 UTC, Jim Cook <jcook@strobeda ta.com>
        >wrote:
        >[color=green]
        >> We have a macro which takes various index constants as an argument and
        >> offsets into an array. The macro can be an Lvalue or Rvalue. The index
        >> is not zero based. I would like a compile time error displayed if the
        >> index is out of bounds. Is there a way to do this well?
        >>
        >> I read through the FAQ that I could find mentioned, and did not see this
        >> sort of question in the preprocessor section anywhere.
        >>
        >> What I came up with is below. It does in fact generate errors using my
        >> 16-bit and 32-bit compilers, but it feels like I'm relying a little bit
        >> on the behavior of the MS compiler and would like to know a portable
        >> proper C method.
        >>
        >> Thank you for any time spent on this.
        >>
        >>
        >> The code and run-time clip are below. The valid index range is from 20
        >> to 27 inclusive. Any other value should produce an error. Testing for
        >> non-integer is not required.
        >>
        >>
        >> // #define argument range testing
        >> #include "limits.h"
        >>
        >> volatile unsigned short usArray[8];
        >> volatile unsigned short usI;
        >> volatile unsigned char ucArray[8];
        >> volatile unsigned char ucI;
        >>
        >> #define usAccess(r) \
        >> (usArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])[/color][/color]
        ^^^^^[color=blue][color=green]
        >> #define ucAccess(r) \
        >> (ucArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])[/color][/color]
        ^^^^^[color=blue]
        >
        >Both macros are wrong!
        >Please name a number that can be in r that is both, <= 20 AND >= 27 as[/color]
        ^^^^^^
        Once again, you failed to read carefully.
        [color=blue]
        >required to get TRUE from the ?: operator.
        >The numbers 21 trough 26 in r will fail on && because the left hand
        >side of && will tell FALSE and the right hand side will never been
        >tested.[/color]
        This is plain wrong.

        20 <= 20 && 20 <= 27 /* TRUE */
        20 <= 21 && 21 <= 27 /* TRUE */
        20 <= 22 && 22 <= 27 /* TRUE */
        20 <= 23 && 23 <= 27 /* TRUE */
        20 <= 24 && 24 <= 27 /* TRUE */
        20 <= 25 && 25 <= 27 /* TRUE */
        20 <= 26 && 26 <= 27 /* TRUE */
        20 <= 27 && 27 <= 27 /* TRUE */

        Quod erat demonstrandum.

        <SNIP>

        Irrwahn
        --
        6 * 9 = 42 (base 13)

        Comment

        • Kevin Easton

          #5
          Re: Macro argument bounds checking?

          Jim Cook <jcook@strobeda ta.com> wrote:[color=blue]
          > We have a macro which takes various index constants as an argument and
          > offsets into an array. The macro can be an Lvalue or Rvalue. The index
          > is not zero based. I would like a compile time error displayed if the
          > index is out of bounds. Is there a way to do this well?
          >
          > I read through the FAQ that I could find mentioned, and did not see this
          > sort of question in the preprocessor section anywhere.
          >
          > What I came up with is below. It does in fact generate errors using my
          > 16-bit and 32-bit compilers, but it feels like I'm relying a little bit
          > on the behavior of the MS compiler and would like to know a portable
          > proper C method.
          >
          > Thank you for any time spent on this.
          >
          >
          > The code and run-time clip are below. The valid index range is from 20
          > to 27 inclusive. Any other value should produce an error. Testing for
          > non-integer is not required.
          >
          >
          > // #define argument range testing
          > #include "limits.h"
          >
          > volatile unsigned short usArray[8];
          > volatile unsigned short usI;
          > volatile unsigned char ucArray[8];
          > volatile unsigned char ucI;
          >
          > #define usAccess(r) \
          > (usArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])
          > #define ucAccess(r) \
          > (ucArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])[/color]

          Try:

          #define usAccess(n) \
          (*((void)(sizeo f(int[((n) < 0 || (n) > 10) ? -1 : 1])), &usArray[n]))

          The idea being to force a compile-time error by attempting to take the
          sizeof an array with negative size. (I can't recall if conditional
          operators are prohibited in compile-time constants - if so, just
          multiple the result of the || operator by -2 and add 1). Methods based
          on arrays of zero size won't error on gcc in non-pedantic mode, and
          possibly other compilers.

          - Kevin.

          Comment

          • Capstar

            #6
            Re: Macro argument bounds checking?

            The Real OS/2 Guy wrote:[color=blue][color=green]
            >>
            >>#define usAccess(r) \
            >> (usArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])
            >>#define ucAccess(r) \
            >> (ucArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])[/color]
            >
            >
            > Both macros are wrong!
            > Please name a number that can be in r that is both, <= 20 AND >= 27 as
            > required to get TRUE from the ?: operator.[/color]

            But that's not what the macro states. It states:

            (20<=r && r<=27 ? 0 : INT_MAX)

            which is the same as:

            (r>=20 && r<=27 ? 0 : INT_MAX)

            So in my opinion that means r must be bigger or equal than 20 AND
            smaller or equal than 27, which is not impossible if you ask me.

            Mark

            Comment

            • Jim Cook

              #7
              Re: Macro argument bounds checking?

              > Here is something that will fail more reliably, and should[color=blue]
              > work I think (though it's at the limits of the standard):
              >
              > volatile unsigned short usArray[8];
              >
              > #define usAccess(r) \
              > ( *((unsigned short(*)[20<=r&&r<=27])usArray)[r-20] )[/color]

              Thank you. This is not only a perfect solution, but teaches me something
              as well. I see also that Kevin gave essentially the same answer.

              I would also like to thank Irrwahn and Mark for replying to the person
              who did not read the question very carefully.

              Comment

              • Dave Thompson

                #8
                Re: Macro argument bounds checking?

                On Wed, 17 Sep 2003 11:41:32 +0200, "Ivan Vecerina"
                <NONE_use_form@ website_to_cont act_me> wrote:
                [color=blue]
                > "Jim Cook" <jcook@strobeda ta.com> wrote in message
                > news:bk880s$k42 @dispatch.conce ntric.net...
                > | We have a macro which takes various index constants as an argument and
                > | offsets into an array. The macro can be an Lvalue or Rvalue. The index
                > | is not zero based. I would like a compile time error displayed if the
                > | index is out of bounds. Is there a way to do this well?
                > ...
                > | volatile unsigned short usArray[8];
                > | volatile unsigned short usI;
                > ...
                > | #define usAccess(r) \
                > | (usArray[r-20 + 2*(20<=r && r<=27 ? 0 : INT_MAX)])
                >
                > Here is something that will fail more reliably, and should
                > work I think (though it's at the limits of the standard):
                >
                > volatile unsigned short usArray[8];
                >
                > #define usAccess(r) \
                > ( *((unsigned short(*)[20<=r&&r<=27])usArray)[r-20] )[/color]
                <snip>[color=blue]
                > The idea is that the conditional expression is used
                > to define the size of an array ( 0 or 1 ).
                > The following type expression defines a pointer
                > to that array:
                > (unsigned short(*)[20<=r&&r<=27])
                >
                > A pointer of that type is then used to perform offset
                > calculations. And this will fail/be illegal if the array
                > size is zero (some compilers may even complain before).
                >[/color]
                It is a constraint violation (if constant, see below) and must be
                diagnosed in a conforming compiler, but it need not fail to compile.
                At least gcc supports zero-bound arrays as an extension, and by
                default (if you don't ask for -pedantic[-errors]) silently. Using -1
                instead of 0, as Kevin Easton does elsethread, will fix that part; it
                is possible (though IMO not that useful) to define consistent
                semantics for zero bound, but not for negative bound.

                Also, for in-range values this effectively changes (reduces) the type
                of the lvalue used to unsigned short [1], so any access other than to
                (20) is arguably technically illegal -- is the "array object" in 6.5.6
                the designated one or the underlying one? -- plus the nominal
                dereference violates 6.5.p7. It is exceedingly (vanishingly?) rare
                for a C implementation to enforce these as long as the actual access
                to the actual object is valid, and in fact usually as long as the
                actual access is just to usable memory; but it legally could. To be
                absolutely safe, and also clear, I would use [valid? actualbound: -1].
                And yes, ?: is permitted in constant expressions; assignments are not,
                and fetches (thus, any objects) are not in most cases; neither is
                comma operator, perhaps on grounds that it was envisioned (only) to
                allow side-effects like assignment, but that is an easy extension.

                Plus an implementation "may accept other forms of constant
                expressions" (6.6p10) although it would be pretty perverse to accept
                things that are not in fact compile-time constant; I suspect this was
                intended to allow things like sin(), and maybe (pure) functions for
                which a definition is available and can be evaluated at compile time,
                even though general function calls are prohibited. It could also be
                used in C90 to allow not-really-constant aggregate local initializers;
                C99 allows this directly.
                [color=blue]
                > A key feature of this approach is that it will fail
                > to compile if the parameter of usAccess is not a compile-
                > time constant (while the macro you posted would just
                > lead to undefined behavior...).
                >[/color]
                In C90 it must be diagnosed if not constant (see above). In C99 it
                (quietly!) becomes a VLA and if the bound evaluates to <= 0 it is
                AFAICT only UB (6.2.5p20) and need not be diagnosed.

                Bitfield sizes and enumerator explicit values must still be integer
                constant expressions, and the former must be nonnegative and with a
                name strictly positive but <= an implementation defined limit, in all
                cases as constraint violations which must be diagnosed. And even gcc,
                at least as of 2.95.2, hasn't extended these. Also subscripts in a
                designated initializer, but only in C99; and case labels and #[el]if
                expressions, but there's no (standard) way to use those in an
                expression and thus the desired macro.

                - David.Thompson1 at worldnet.att.ne t

                Comment

                Working...