0

I was looking at some concise example code showing how to copy a float without running into memory alignment issues on a specific hardware platform. I noticed it didn't make use of sizeof():

uint8_t mybuffer[4];
float f;
memcpy(&f, mybuffer, 4)

The next question is what to use sizeof() on which leads to Microsoft's partial answer of memcpy_s but that's a run-time approach and requires return checks.

Going a little beyond the code snippet I cited, for the specific case where the size of source and destination can be determined correctly at compile-time, is there a compact/single line (e.g. to keep that example code short) equivalent of memcpy() that ensures that the lengths are identical and terminates compilation with a helpful message if not? One that avoids the pitfalls of ARR01-C. Do not apply the sizeof operator to a pointer when taking the size of an array would be nice.

There's some overlap here with memcpy(), what should the value of the size parameter be?

KevinJWalters
  • 193
  • 1
  • 7
  • If the array `mybuffer` is passed as a parameter to a function, it will decay to a pointer and any size information will be lost. So I do not think there is any way to do this in a general way. – Rishikesh Raje Jul 04 '19 at 10:40
  • You can use a static assertion: `_Static_assert(sizeof f == sizeof mybuffer, "float is not same size as buffer.");` – Eric Postpischil Jul 04 '19 at 11:22
  • @RishikeshRaje: The question does not ask for a way to know the length of a buffer given only a pointer to its first element. It asks for a way to ensure the sizes of two objects are satisfactory. – Eric Postpischil Jul 04 '19 at 11:23
  • @EricPostpischil - I believe the OP wants to know the size of `mybufer` and whether the array size is 4. This can only be determined if the array has not decayed to a pointer by passing to a function. – Rishikesh Raje Jul 04 '19 at 11:53
  • @EricPostpischil The pointer discussion has come up because a good solution here would deal with aforementioned ARR01-C issue. – KevinJWalters Jul 04 '19 at 12:37
  • How about memcpy( &f, mybuffer, sizeof f); ? – dmuir Jul 04 '19 at 13:11
  • [Read this](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1967.htm) before you even think of using Microsoft's `*_s()` functions: "Microsoft Visual Studio implements an early version of the APIs. However, the implementation is incomplete and conforms neither to C11 nor to the original TR 24731-1. ... As a result of the numerous deviations from the specification the Microsoft implementation cannot be considered conforming or portable." – Andrew Henle Jul 04 '19 at 13:36
  • @RishikeshRaje: You may believe that OP wants to know the size of an array passed by pointer, but what facts is this belief grounded in? The question specifically asks “… for **the specific case where the size of source and destination can be determined correctly at compile-time**, is there a…” In other words, it explicitly states a premise that the sizes are known, and hence they do not need to be derived from a pointer or other information. The problem is then one of providing a compile-time test, to which the obvious solution is a `_Static_assert`. – Eric Postpischil Jul 04 '19 at 22:15

2 Answers2

3
  • What's the modern, portable, safe equivalent of memcpy with compile-time checks in C?

    memcpy is modern, portable and safe. There exists no replacement that you should use instead.

    If you use Microsoft logic, it is memcpy's fault that incompetent programmers pass null pointers to it. But such bugs should be fixed by sanity checking the input before passing it on, not by changing memcpy.

    memcpy_s is to be regarded as deprecated and everything from the C11 bounds-checking interface should be avoided, since there's non-existent compiler support for it. Dangerous compilers such as Visual Studio has also made it non-portable, since they don't even follow C standard Annex K, but invent their own non-compatible version of it. My advise is to regard every function ending with a _s as a safety hazard.

  • is there a compact/single line (e.g. to keep that example code short) equivalent of memcpy() that ensures that the lengths are identical and terminates compilation with a helpful message if not?

    Yes. memcpy(&f, mybuffer, sizeof(f))

  • One that avoids the pitfalls of ARR01-C. Do not apply the sizeof operator to a pointer when taking the size of an array would be nice

    That CERT rule is only there for programmers who don't know how array decay of function parameters work. The solution to that is to educate people, rather than assuming that every programmer using your code base is incompetent. Besides, this is a typical bug that static analysers will catch - to enable static analysis might actually be the very reason why the CERT rule is there.

    You can use sizeof inside functions if you know what you are doing. Every function taking an array as parameter also takes a size. Use that size. So if you have this: void foo (size_t n, float array[n]); then you can do sizeof(float[n]) inside that function just fine.

    As an alternative, there's another somewhat obscure version, sometimes promoted by safety standards. If you change the function to use an array pointer, void foo (size_t n, float(*array)[n]), you can do sizeof(*array) inside that function. We can also use array pointers to increase type safety:

    void foo (float(*array)[5]);
    ...
    
    float arr[4];
    foo(&arr); // compiler error
    

    The down sides: much more complex syntax and we lose the ability to make const correct parameters.

Lundin
  • 195,001
  • 40
  • 254
  • 396
1

The memcpy() function is perfectly fine. The C11 bounds checking functions were introduced as part of reducing the chance of buffer overflow defects.

Document N1967, Field Experience With Annex K — Bounds Checking Interfaces, in the section Unnecessary Uses has this to say:

A widespread fallacy originated by Microsoft's deprecation of the standard functions [DEPR] in an effort to increase the adoption of the APIs is that every call to the standard functions is necessarily unsafe and should be replaced by one to the "safer" API. As a result, security-minded teams sometimes naively embark on months-long projects rewriting their working code and dutifully replacing all instances of the "deprecated" functions with the corresponding APIs. This not only leads to unnecessary churn and raises the risk of injecting new bugs into correct code, it also makes the rewritten code less efficient.

The sizeof operator is a compile time operator which means that the size calculation is done by what ever information is available to the compiler at the time of compilation. This is why using the operator can be problematic and a source of runtime errors when used with pointers and arrays. Increasing the warning level of the compiler and using a static code analysis tool can help find these problem areas.

Because the sizeof operator is a compile time operator, there isn't a much you can do for a compiler error or warning though your particular compiler may have a warning level you can set that will do this type of check.

What I have done is to have a Preprocessor macro that I can use for a run time check in debug builds or special release builds used in verification that will be removed when doing a release build for production in order to remove the checking overhead.

So if you have source such as:

uint8_t mybuffer[4];
float f;
memcpy(&f, mybuffer, sizeof(mybuffer));

that will be have the correct number of bytes because the array mybuffer[4] along with its actual size is available to the compiler.

However I would actually prefer the following modification by specifying the size of the target for the memcpy() instead. This ensures that there will not be a buffer overflow even if the source size is incorrect.

uint8_t mybuffer[4];
float f;
memcpy(&f, mybuffer, sizeof(f));

where problems with the sizeof operator develop is when the compiler is unable to deduce the size of the array or the size of the object whose address is in a pointer. Using it with array declarations of function arguments isn't safe either.

If you have a function int xxx (int a[5]) and in that function you try using the sizeof operator to get the array size in bytes, what you will probably get is the size of an int * instead.

Richard Chambers
  • 16,643
  • 4
  • 81
  • 106