Implementation of memset_s based on C11 standard
$begingroup$
The following code attempts to implement memset_s based on the ISO/IEC 9899:201x N1570 draft:
#include <stdio.h>
#include <string.h>
#include <stdint.h>
errno_t memset_s(void *s,rsize_t smax, int c, rsize_t n)
{
errno_t violation_present = 0;
volatile unsigned char * v = s;
if ( s == NULL )
{
fprintf(stderr,"memset_s: Error: void * s == NULL!n");
return 1;
}
if ( n > RSIZE_MAX )
{
fprintf(stderr,"memset_s: Warning: rsize_t n > RSIZE_MAX!n");
violation_present = 1;
}
if ( n > smax )
{
fprintf(stderr,"memset_s: Warning: rsize_t n > rsize_t smax!n");
violation_present = 1;
}
if ( smax > RSIZE_MAX )
{
fprintf(stderr,"memset_s: Error: rsize_t smax > RSIZE_MAX!n");
return 1;
}
volatile unsigned char * v_p = &v[0];
rsize_t i = 0;
if ( violation_present == 1 ) // && (s != NULL) && (smax <= RSIZE_MAX) )
{
i = 0;
while ( i < smax )
{
*v_p++ = (unsigned char)c;
i++;
}
return violation_present;
}
else // no runtime-constraint violation found
{
i = 0;
while ( i < n )
{
*v_p++ = (unsigned char)c;
i++;
}
return violation_present;
}
}
I have also made a C source test file for memset_s:
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#define ARRAY_SIZE 8
typedef struct Node
{
struct Node * link;
int val;
} Node;
int main(void)
{
errno_t result = 0;
printf("This program only checks for runtime-constraintn"
"violations in an invocation of memset_s.nn"
);
static char test[ARRAY_SIZE];
printf("Does memset_s return nonzero value when void * s"
" == NULL?nn"
);
result = memset_s(NULL,sizeof(test),0,sizeof(test));
printf("Return Value: %llunn",result);
printf("Does memset_s return nonzero value when smax >"
" RSIZE_MAX?nn"
);
result = memset_s(test,RSIZE_MAX+1,0,sizeof(test));
printf("Return Value: %llunn",result);
printf("Does memset_s set the inputted char value inton"
"each of the first smax characters of the objectn"
"pointed to by void *s only when there is an"
"violation and void * s != NULl and when rsize_tn"
"smax is less than or equal to RSIZE_MAX and returnn"
"nonzero value?nn"
);
result = memset_s(test,8*sizeof(char),84,RSIZE_MAX+1);
printf("Return Value: %llunn",result);
printf("test string set with memset_s:n%sn",test);
for ( rsize_t i = 0; i < ARRAY_SIZE; i++)
{
test[i] = '';
}
printf("Does memset_s correctly set the inputted char valuen"
"into each of the first n characters of the objectn"
"pointed to by void *s when there is NO runtimen"
"constraint violation?nn"
);
result = memset_s(test,8*sizeof(char),84,4*sizeof(char));
printf("Return Value: %llunn",result);
printf("test string set with memset_s for first four charn"
"elements in a char array of 8 elements:n%snn",
test
);
printf("Does memset_s only set the first smax values whenn"
"rsize_t n > rsize_t smax?nn"
);
for ( rsize_t i = 0; i < ARRAY_SIZE; i++)
{
test[i] = '';
}
result = memset_s(test,8*sizeof(char),84,8*sizeof(char)+1);
printf("Return Value: %llunn",result);
printf("test string below:n%snn",
test
);
printf("Does memset_s correctly allocate unsigned chars to objectsn"
"that in turn, store other objects, like a struct?n"
"In the example below, a struct Node of a Linked Listn"
"is initialized through memset_snn"
);
Node * node;
result = memset_s(node,sizeof(Node),0,sizeof(Node));
printf("Return Value: %llunn",result);
printf("node->link == %pn",node->link);
printf("node->val == %dnn",node->val);
printf("Does memset_s do what was tested previously except thatn"
"it initializes with a nonzero unsigned char value? In then"
"example below, a second struct Node name node_two isn"
"initialized with the unsigned char value 84nn"
);
Node * node_two;
Node * node_three;
result = memset_s(node_two,sizeof(Node),84,sizeof(Node));
printf("Return Value: %llunn",result);
printf("node_two->link == %pn",node_two->link);
printf("node_two->val == %dnn",node_two->val);
printf("node_two->val in Hexadecimal format == 0x%xnn",node_two->val);
return 0;
}
My only concern is that I forgot to find a security flaw in my implementation of memset_s. Did I forget to test for any values?
I plan to use memset_s in my implementation of http.c from the book "Implementing SSL/TLS Using Cryptography and PKI" by Joshua Davies.
c memory-management c11
New contributor
$endgroup$
add a comment |
$begingroup$
The following code attempts to implement memset_s based on the ISO/IEC 9899:201x N1570 draft:
#include <stdio.h>
#include <string.h>
#include <stdint.h>
errno_t memset_s(void *s,rsize_t smax, int c, rsize_t n)
{
errno_t violation_present = 0;
volatile unsigned char * v = s;
if ( s == NULL )
{
fprintf(stderr,"memset_s: Error: void * s == NULL!n");
return 1;
}
if ( n > RSIZE_MAX )
{
fprintf(stderr,"memset_s: Warning: rsize_t n > RSIZE_MAX!n");
violation_present = 1;
}
if ( n > smax )
{
fprintf(stderr,"memset_s: Warning: rsize_t n > rsize_t smax!n");
violation_present = 1;
}
if ( smax > RSIZE_MAX )
{
fprintf(stderr,"memset_s: Error: rsize_t smax > RSIZE_MAX!n");
return 1;
}
volatile unsigned char * v_p = &v[0];
rsize_t i = 0;
if ( violation_present == 1 ) // && (s != NULL) && (smax <= RSIZE_MAX) )
{
i = 0;
while ( i < smax )
{
*v_p++ = (unsigned char)c;
i++;
}
return violation_present;
}
else // no runtime-constraint violation found
{
i = 0;
while ( i < n )
{
*v_p++ = (unsigned char)c;
i++;
}
return violation_present;
}
}
I have also made a C source test file for memset_s:
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#define ARRAY_SIZE 8
typedef struct Node
{
struct Node * link;
int val;
} Node;
int main(void)
{
errno_t result = 0;
printf("This program only checks for runtime-constraintn"
"violations in an invocation of memset_s.nn"
);
static char test[ARRAY_SIZE];
printf("Does memset_s return nonzero value when void * s"
" == NULL?nn"
);
result = memset_s(NULL,sizeof(test),0,sizeof(test));
printf("Return Value: %llunn",result);
printf("Does memset_s return nonzero value when smax >"
" RSIZE_MAX?nn"
);
result = memset_s(test,RSIZE_MAX+1,0,sizeof(test));
printf("Return Value: %llunn",result);
printf("Does memset_s set the inputted char value inton"
"each of the first smax characters of the objectn"
"pointed to by void *s only when there is an"
"violation and void * s != NULl and when rsize_tn"
"smax is less than or equal to RSIZE_MAX and returnn"
"nonzero value?nn"
);
result = memset_s(test,8*sizeof(char),84,RSIZE_MAX+1);
printf("Return Value: %llunn",result);
printf("test string set with memset_s:n%sn",test);
for ( rsize_t i = 0; i < ARRAY_SIZE; i++)
{
test[i] = '';
}
printf("Does memset_s correctly set the inputted char valuen"
"into each of the first n characters of the objectn"
"pointed to by void *s when there is NO runtimen"
"constraint violation?nn"
);
result = memset_s(test,8*sizeof(char),84,4*sizeof(char));
printf("Return Value: %llunn",result);
printf("test string set with memset_s for first four charn"
"elements in a char array of 8 elements:n%snn",
test
);
printf("Does memset_s only set the first smax values whenn"
"rsize_t n > rsize_t smax?nn"
);
for ( rsize_t i = 0; i < ARRAY_SIZE; i++)
{
test[i] = '';
}
result = memset_s(test,8*sizeof(char),84,8*sizeof(char)+1);
printf("Return Value: %llunn",result);
printf("test string below:n%snn",
test
);
printf("Does memset_s correctly allocate unsigned chars to objectsn"
"that in turn, store other objects, like a struct?n"
"In the example below, a struct Node of a Linked Listn"
"is initialized through memset_snn"
);
Node * node;
result = memset_s(node,sizeof(Node),0,sizeof(Node));
printf("Return Value: %llunn",result);
printf("node->link == %pn",node->link);
printf("node->val == %dnn",node->val);
printf("Does memset_s do what was tested previously except thatn"
"it initializes with a nonzero unsigned char value? In then"
"example below, a second struct Node name node_two isn"
"initialized with the unsigned char value 84nn"
);
Node * node_two;
Node * node_three;
result = memset_s(node_two,sizeof(Node),84,sizeof(Node));
printf("Return Value: %llunn",result);
printf("node_two->link == %pn",node_two->link);
printf("node_two->val == %dnn",node_two->val);
printf("node_two->val in Hexadecimal format == 0x%xnn",node_two->val);
return 0;
}
My only concern is that I forgot to find a security flaw in my implementation of memset_s. Did I forget to test for any values?
I plan to use memset_s in my implementation of http.c from the book "Implementing SSL/TLS Using Cryptography and PKI" by Joshua Davies.
c memory-management c11
New contributor
$endgroup$
$begingroup$
You have a bunch of commented-out code in there. Please either uncomment it (if it's meant to be part of the code under review) or delete it (if it's meant not to be part of the code under review).
$endgroup$
– Quuxplusone
2 hours ago
$begingroup$
Thanks Quuxplusone. Sure.
$endgroup$
– Tanveer Salim
1 hour ago
add a comment |
$begingroup$
The following code attempts to implement memset_s based on the ISO/IEC 9899:201x N1570 draft:
#include <stdio.h>
#include <string.h>
#include <stdint.h>
errno_t memset_s(void *s,rsize_t smax, int c, rsize_t n)
{
errno_t violation_present = 0;
volatile unsigned char * v = s;
if ( s == NULL )
{
fprintf(stderr,"memset_s: Error: void * s == NULL!n");
return 1;
}
if ( n > RSIZE_MAX )
{
fprintf(stderr,"memset_s: Warning: rsize_t n > RSIZE_MAX!n");
violation_present = 1;
}
if ( n > smax )
{
fprintf(stderr,"memset_s: Warning: rsize_t n > rsize_t smax!n");
violation_present = 1;
}
if ( smax > RSIZE_MAX )
{
fprintf(stderr,"memset_s: Error: rsize_t smax > RSIZE_MAX!n");
return 1;
}
volatile unsigned char * v_p = &v[0];
rsize_t i = 0;
if ( violation_present == 1 ) // && (s != NULL) && (smax <= RSIZE_MAX) )
{
i = 0;
while ( i < smax )
{
*v_p++ = (unsigned char)c;
i++;
}
return violation_present;
}
else // no runtime-constraint violation found
{
i = 0;
while ( i < n )
{
*v_p++ = (unsigned char)c;
i++;
}
return violation_present;
}
}
I have also made a C source test file for memset_s:
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#define ARRAY_SIZE 8
typedef struct Node
{
struct Node * link;
int val;
} Node;
int main(void)
{
errno_t result = 0;
printf("This program only checks for runtime-constraintn"
"violations in an invocation of memset_s.nn"
);
static char test[ARRAY_SIZE];
printf("Does memset_s return nonzero value when void * s"
" == NULL?nn"
);
result = memset_s(NULL,sizeof(test),0,sizeof(test));
printf("Return Value: %llunn",result);
printf("Does memset_s return nonzero value when smax >"
" RSIZE_MAX?nn"
);
result = memset_s(test,RSIZE_MAX+1,0,sizeof(test));
printf("Return Value: %llunn",result);
printf("Does memset_s set the inputted char value inton"
"each of the first smax characters of the objectn"
"pointed to by void *s only when there is an"
"violation and void * s != NULl and when rsize_tn"
"smax is less than or equal to RSIZE_MAX and returnn"
"nonzero value?nn"
);
result = memset_s(test,8*sizeof(char),84,RSIZE_MAX+1);
printf("Return Value: %llunn",result);
printf("test string set with memset_s:n%sn",test);
for ( rsize_t i = 0; i < ARRAY_SIZE; i++)
{
test[i] = '';
}
printf("Does memset_s correctly set the inputted char valuen"
"into each of the first n characters of the objectn"
"pointed to by void *s when there is NO runtimen"
"constraint violation?nn"
);
result = memset_s(test,8*sizeof(char),84,4*sizeof(char));
printf("Return Value: %llunn",result);
printf("test string set with memset_s for first four charn"
"elements in a char array of 8 elements:n%snn",
test
);
printf("Does memset_s only set the first smax values whenn"
"rsize_t n > rsize_t smax?nn"
);
for ( rsize_t i = 0; i < ARRAY_SIZE; i++)
{
test[i] = '';
}
result = memset_s(test,8*sizeof(char),84,8*sizeof(char)+1);
printf("Return Value: %llunn",result);
printf("test string below:n%snn",
test
);
printf("Does memset_s correctly allocate unsigned chars to objectsn"
"that in turn, store other objects, like a struct?n"
"In the example below, a struct Node of a Linked Listn"
"is initialized through memset_snn"
);
Node * node;
result = memset_s(node,sizeof(Node),0,sizeof(Node));
printf("Return Value: %llunn",result);
printf("node->link == %pn",node->link);
printf("node->val == %dnn",node->val);
printf("Does memset_s do what was tested previously except thatn"
"it initializes with a nonzero unsigned char value? In then"
"example below, a second struct Node name node_two isn"
"initialized with the unsigned char value 84nn"
);
Node * node_two;
Node * node_three;
result = memset_s(node_two,sizeof(Node),84,sizeof(Node));
printf("Return Value: %llunn",result);
printf("node_two->link == %pn",node_two->link);
printf("node_two->val == %dnn",node_two->val);
printf("node_two->val in Hexadecimal format == 0x%xnn",node_two->val);
return 0;
}
My only concern is that I forgot to find a security flaw in my implementation of memset_s. Did I forget to test for any values?
I plan to use memset_s in my implementation of http.c from the book "Implementing SSL/TLS Using Cryptography and PKI" by Joshua Davies.
c memory-management c11
New contributor
$endgroup$
The following code attempts to implement memset_s based on the ISO/IEC 9899:201x N1570 draft:
#include <stdio.h>
#include <string.h>
#include <stdint.h>
errno_t memset_s(void *s,rsize_t smax, int c, rsize_t n)
{
errno_t violation_present = 0;
volatile unsigned char * v = s;
if ( s == NULL )
{
fprintf(stderr,"memset_s: Error: void * s == NULL!n");
return 1;
}
if ( n > RSIZE_MAX )
{
fprintf(stderr,"memset_s: Warning: rsize_t n > RSIZE_MAX!n");
violation_present = 1;
}
if ( n > smax )
{
fprintf(stderr,"memset_s: Warning: rsize_t n > rsize_t smax!n");
violation_present = 1;
}
if ( smax > RSIZE_MAX )
{
fprintf(stderr,"memset_s: Error: rsize_t smax > RSIZE_MAX!n");
return 1;
}
volatile unsigned char * v_p = &v[0];
rsize_t i = 0;
if ( violation_present == 1 ) // && (s != NULL) && (smax <= RSIZE_MAX) )
{
i = 0;
while ( i < smax )
{
*v_p++ = (unsigned char)c;
i++;
}
return violation_present;
}
else // no runtime-constraint violation found
{
i = 0;
while ( i < n )
{
*v_p++ = (unsigned char)c;
i++;
}
return violation_present;
}
}
I have also made a C source test file for memset_s:
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#define ARRAY_SIZE 8
typedef struct Node
{
struct Node * link;
int val;
} Node;
int main(void)
{
errno_t result = 0;
printf("This program only checks for runtime-constraintn"
"violations in an invocation of memset_s.nn"
);
static char test[ARRAY_SIZE];
printf("Does memset_s return nonzero value when void * s"
" == NULL?nn"
);
result = memset_s(NULL,sizeof(test),0,sizeof(test));
printf("Return Value: %llunn",result);
printf("Does memset_s return nonzero value when smax >"
" RSIZE_MAX?nn"
);
result = memset_s(test,RSIZE_MAX+1,0,sizeof(test));
printf("Return Value: %llunn",result);
printf("Does memset_s set the inputted char value inton"
"each of the first smax characters of the objectn"
"pointed to by void *s only when there is an"
"violation and void * s != NULl and when rsize_tn"
"smax is less than or equal to RSIZE_MAX and returnn"
"nonzero value?nn"
);
result = memset_s(test,8*sizeof(char),84,RSIZE_MAX+1);
printf("Return Value: %llunn",result);
printf("test string set with memset_s:n%sn",test);
for ( rsize_t i = 0; i < ARRAY_SIZE; i++)
{
test[i] = '';
}
printf("Does memset_s correctly set the inputted char valuen"
"into each of the first n characters of the objectn"
"pointed to by void *s when there is NO runtimen"
"constraint violation?nn"
);
result = memset_s(test,8*sizeof(char),84,4*sizeof(char));
printf("Return Value: %llunn",result);
printf("test string set with memset_s for first four charn"
"elements in a char array of 8 elements:n%snn",
test
);
printf("Does memset_s only set the first smax values whenn"
"rsize_t n > rsize_t smax?nn"
);
for ( rsize_t i = 0; i < ARRAY_SIZE; i++)
{
test[i] = '';
}
result = memset_s(test,8*sizeof(char),84,8*sizeof(char)+1);
printf("Return Value: %llunn",result);
printf("test string below:n%snn",
test
);
printf("Does memset_s correctly allocate unsigned chars to objectsn"
"that in turn, store other objects, like a struct?n"
"In the example below, a struct Node of a Linked Listn"
"is initialized through memset_snn"
);
Node * node;
result = memset_s(node,sizeof(Node),0,sizeof(Node));
printf("Return Value: %llunn",result);
printf("node->link == %pn",node->link);
printf("node->val == %dnn",node->val);
printf("Does memset_s do what was tested previously except thatn"
"it initializes with a nonzero unsigned char value? In then"
"example below, a second struct Node name node_two isn"
"initialized with the unsigned char value 84nn"
);
Node * node_two;
Node * node_three;
result = memset_s(node_two,sizeof(Node),84,sizeof(Node));
printf("Return Value: %llunn",result);
printf("node_two->link == %pn",node_two->link);
printf("node_two->val == %dnn",node_two->val);
printf("node_two->val in Hexadecimal format == 0x%xnn",node_two->val);
return 0;
}
My only concern is that I forgot to find a security flaw in my implementation of memset_s. Did I forget to test for any values?
I plan to use memset_s in my implementation of http.c from the book "Implementing SSL/TLS Using Cryptography and PKI" by Joshua Davies.
c memory-management c11
c memory-management c11
New contributor
New contributor
edited 31 mins ago
Quuxplusone
12.5k12061
12.5k12061
New contributor
asked 3 hours ago
Tanveer SalimTanveer Salim
162
162
New contributor
New contributor
$begingroup$
You have a bunch of commented-out code in there. Please either uncomment it (if it's meant to be part of the code under review) or delete it (if it's meant not to be part of the code under review).
$endgroup$
– Quuxplusone
2 hours ago
$begingroup$
Thanks Quuxplusone. Sure.
$endgroup$
– Tanveer Salim
1 hour ago
add a comment |
$begingroup$
You have a bunch of commented-out code in there. Please either uncomment it (if it's meant to be part of the code under review) or delete it (if it's meant not to be part of the code under review).
$endgroup$
– Quuxplusone
2 hours ago
$begingroup$
Thanks Quuxplusone. Sure.
$endgroup$
– Tanveer Salim
1 hour ago
$begingroup$
You have a bunch of commented-out code in there. Please either uncomment it (if it's meant to be part of the code under review) or delete it (if it's meant not to be part of the code under review).
$endgroup$
– Quuxplusone
2 hours ago
$begingroup$
You have a bunch of commented-out code in there. Please either uncomment it (if it's meant to be part of the code under review) or delete it (if it's meant not to be part of the code under review).
$endgroup$
– Quuxplusone
2 hours ago
$begingroup$
Thanks Quuxplusone. Sure.
$endgroup$
– Tanveer Salim
1 hour ago
$begingroup$
Thanks Quuxplusone. Sure.
$endgroup$
– Tanveer Salim
1 hour ago
add a comment |
1 Answer
1
active
oldest
votes
$begingroup$
errno_t memset_s(void *s,rsize_t smax, int c, rsize_t n)
You've got a missing space after that first comma. (Sure, inconsistent whitespace doesn't affect functionality; but like any spelling mistake, it indicates that you haven't proofread your code. Which means it probably has some other one-character typos too. Some of which might affect functionality. Always read your code after writing it!)
The typedef errno_t
is invariably int
. So personally I'd just write int
— unless you were planning to use the association with errno
in some significant way. Right now your code just returns 0
on success and 1
(which is to say EPERM
) on failure. Consider whether memset_s
should return a more specific and helpful errno than simply EPERM
. For example, maybe if you pass NULL to it, you should get back EINVAL
"Invalid argument"?
However, at the same time, YAGNI — it may be counterproductive to spend a lot of time agonizing over what are the proper errno return values, unless you have an actual use-case for these return values. The only reason to return EINVAL
for a null argument instead of the generic EPERM
is that it enables a caller to detect that specific error case and handle the error accordingly. But the caller can already detect that case! The caller doesn't need your help to detect s == NULL
! The caller controls the value of s
in the first place, and can easily check for null beforehand, if they think it's possible for s
to be null. (And if it's not possible for s
to be null, then there's no point checking inside memset_s
either. That's just a waste of CPU cycles.)
Can you tell that I think memset_s
is largely a waste of time, design-wise? :)
I find your chain of if
s difficult to match up to the specification. The specification is as follows:
Runtime-constraints:
s
shall not be a null pointer. Neithersmax
norn
shall be greater thanRSIZE_MAX
.n
shall not be greater thansmax
.
If there is a runtime-constraint violation, then if
s
is not a null pointer andsmax
is not greater thanRSIZE_MAX
, thememset_s
function stores the value ofc
(converted to anunsigned char
) into each of the firstsmax
characters of the object pointed to bys
.
Description: The
memset_s
function copies the value ofc
(converted to anunsigned char
) into each of the firstn
characters of the object pointed to bys
.
So I would naturally implement it something like this:
errno_t memset_s(void *s, rsize_t smax, int c, rsize_t n) {
bool violation = (s == NULL) || (smax > RSIZE_MAX) || (n > RSIZE_MAX) || (n > smax);
if (violation) {
if ((s != NULL) && !(smax > RSIZE_MAX)) {
for (rsize_t i = 0; i < smax; ++i) {
((volatile unsigned char*)s)[i] = c;
}
}
return EPERM;
} else {
for (rsize_t i = 0; i < n; ++i) {
((volatile unsigned char*)s)[i] = c;
}
return 0;
}
}
That seems to implement the specification 100% correctly, line for line.
You write the second for
-loop above as
i = 0;
while ( i < n )
{
*v_p++ = (unsigned char)c;
i++;
}
(yes, with two blank lines between the i = 0
and the rest of the loop). That's definitely too much code for a simple for
-loop. Even without doing anything else to your code, you could replace those 9 lines with 3 lines:
for (int i = 0; i < n; ++i) {
*v_p++ = (unsigned char)c;
}
A 66% reduction in lines-of-code is not bad for a day's work!
volatile unsigned char * v = s;
// ...
volatile unsigned char * v_p = &v[0];
You know that &v[0]
is the same thing as v
, right?
rsize_t i = 0;
if ( violation_present == 1 ) // && (s != NULL) && (smax <= RSIZE_MAX) )
{
i = 0;
Three things:
You initialize
i
to0
, and then again initialize it to0
. Are you worried that the first initialization might not have taken? :)The commented-out code is confusing. (I left a comment on the question referring to this and some other commented-out code; you did remove some of it, but left this snippet commented out.) If you meant for this code to take effect, you should uncomment it. If you meant for this code not to take effect, you should just delete it. Don't leave commented-out code hanging around. (If it's for historical interest, you should learn
git
or some other version-control system.)You branch on if
violation_present == 1
. This suggests thatviolation_present
might take on other values, such as0
(which it does) or2
or42
(which it does not). The compiler will likely generate code to compareviolation_present
against the constant1
. But actually all you mean here is "If there's a violation present...", which is idiomatically expressed asif (violation_present) ...
. Furthermore, you should look up thebool
type (defined in<stdbool.h>
) — it's tailor-made for boolean variables that can only ever take on the valuetrue
orfalse
. (Notice the use ofbool
in my reference implementation above.)
fprintf(stderr,"memset_s: Error: void * s == NULL!n");
Missing whitespace again.
Here you have a memset_s
function, whose job is to set a range of bytes to a value... and you've got it pulling in fprintf
from the standard library! Does that seem appropriate to you?
memset_s
is a very low-level function. It should be usable even on embedded systems that have never heard of fprintf
or stderr
. You should find a way to report errors that doesn't involve <stdio.h>
. (Might I suggest errno_t
? :))
$endgroup$
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Tanveer Salim is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f215309%2fimplementation-of-memset-s-based-on-c11-standard%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
errno_t memset_s(void *s,rsize_t smax, int c, rsize_t n)
You've got a missing space after that first comma. (Sure, inconsistent whitespace doesn't affect functionality; but like any spelling mistake, it indicates that you haven't proofread your code. Which means it probably has some other one-character typos too. Some of which might affect functionality. Always read your code after writing it!)
The typedef errno_t
is invariably int
. So personally I'd just write int
— unless you were planning to use the association with errno
in some significant way. Right now your code just returns 0
on success and 1
(which is to say EPERM
) on failure. Consider whether memset_s
should return a more specific and helpful errno than simply EPERM
. For example, maybe if you pass NULL to it, you should get back EINVAL
"Invalid argument"?
However, at the same time, YAGNI — it may be counterproductive to spend a lot of time agonizing over what are the proper errno return values, unless you have an actual use-case for these return values. The only reason to return EINVAL
for a null argument instead of the generic EPERM
is that it enables a caller to detect that specific error case and handle the error accordingly. But the caller can already detect that case! The caller doesn't need your help to detect s == NULL
! The caller controls the value of s
in the first place, and can easily check for null beforehand, if they think it's possible for s
to be null. (And if it's not possible for s
to be null, then there's no point checking inside memset_s
either. That's just a waste of CPU cycles.)
Can you tell that I think memset_s
is largely a waste of time, design-wise? :)
I find your chain of if
s difficult to match up to the specification. The specification is as follows:
Runtime-constraints:
s
shall not be a null pointer. Neithersmax
norn
shall be greater thanRSIZE_MAX
.n
shall not be greater thansmax
.
If there is a runtime-constraint violation, then if
s
is not a null pointer andsmax
is not greater thanRSIZE_MAX
, thememset_s
function stores the value ofc
(converted to anunsigned char
) into each of the firstsmax
characters of the object pointed to bys
.
Description: The
memset_s
function copies the value ofc
(converted to anunsigned char
) into each of the firstn
characters of the object pointed to bys
.
So I would naturally implement it something like this:
errno_t memset_s(void *s, rsize_t smax, int c, rsize_t n) {
bool violation = (s == NULL) || (smax > RSIZE_MAX) || (n > RSIZE_MAX) || (n > smax);
if (violation) {
if ((s != NULL) && !(smax > RSIZE_MAX)) {
for (rsize_t i = 0; i < smax; ++i) {
((volatile unsigned char*)s)[i] = c;
}
}
return EPERM;
} else {
for (rsize_t i = 0; i < n; ++i) {
((volatile unsigned char*)s)[i] = c;
}
return 0;
}
}
That seems to implement the specification 100% correctly, line for line.
You write the second for
-loop above as
i = 0;
while ( i < n )
{
*v_p++ = (unsigned char)c;
i++;
}
(yes, with two blank lines between the i = 0
and the rest of the loop). That's definitely too much code for a simple for
-loop. Even without doing anything else to your code, you could replace those 9 lines with 3 lines:
for (int i = 0; i < n; ++i) {
*v_p++ = (unsigned char)c;
}
A 66% reduction in lines-of-code is not bad for a day's work!
volatile unsigned char * v = s;
// ...
volatile unsigned char * v_p = &v[0];
You know that &v[0]
is the same thing as v
, right?
rsize_t i = 0;
if ( violation_present == 1 ) // && (s != NULL) && (smax <= RSIZE_MAX) )
{
i = 0;
Three things:
You initialize
i
to0
, and then again initialize it to0
. Are you worried that the first initialization might not have taken? :)The commented-out code is confusing. (I left a comment on the question referring to this and some other commented-out code; you did remove some of it, but left this snippet commented out.) If you meant for this code to take effect, you should uncomment it. If you meant for this code not to take effect, you should just delete it. Don't leave commented-out code hanging around. (If it's for historical interest, you should learn
git
or some other version-control system.)You branch on if
violation_present == 1
. This suggests thatviolation_present
might take on other values, such as0
(which it does) or2
or42
(which it does not). The compiler will likely generate code to compareviolation_present
against the constant1
. But actually all you mean here is "If there's a violation present...", which is idiomatically expressed asif (violation_present) ...
. Furthermore, you should look up thebool
type (defined in<stdbool.h>
) — it's tailor-made for boolean variables that can only ever take on the valuetrue
orfalse
. (Notice the use ofbool
in my reference implementation above.)
fprintf(stderr,"memset_s: Error: void * s == NULL!n");
Missing whitespace again.
Here you have a memset_s
function, whose job is to set a range of bytes to a value... and you've got it pulling in fprintf
from the standard library! Does that seem appropriate to you?
memset_s
is a very low-level function. It should be usable even on embedded systems that have never heard of fprintf
or stderr
. You should find a way to report errors that doesn't involve <stdio.h>
. (Might I suggest errno_t
? :))
$endgroup$
add a comment |
$begingroup$
errno_t memset_s(void *s,rsize_t smax, int c, rsize_t n)
You've got a missing space after that first comma. (Sure, inconsistent whitespace doesn't affect functionality; but like any spelling mistake, it indicates that you haven't proofread your code. Which means it probably has some other one-character typos too. Some of which might affect functionality. Always read your code after writing it!)
The typedef errno_t
is invariably int
. So personally I'd just write int
— unless you were planning to use the association with errno
in some significant way. Right now your code just returns 0
on success and 1
(which is to say EPERM
) on failure. Consider whether memset_s
should return a more specific and helpful errno than simply EPERM
. For example, maybe if you pass NULL to it, you should get back EINVAL
"Invalid argument"?
However, at the same time, YAGNI — it may be counterproductive to spend a lot of time agonizing over what are the proper errno return values, unless you have an actual use-case for these return values. The only reason to return EINVAL
for a null argument instead of the generic EPERM
is that it enables a caller to detect that specific error case and handle the error accordingly. But the caller can already detect that case! The caller doesn't need your help to detect s == NULL
! The caller controls the value of s
in the first place, and can easily check for null beforehand, if they think it's possible for s
to be null. (And if it's not possible for s
to be null, then there's no point checking inside memset_s
either. That's just a waste of CPU cycles.)
Can you tell that I think memset_s
is largely a waste of time, design-wise? :)
I find your chain of if
s difficult to match up to the specification. The specification is as follows:
Runtime-constraints:
s
shall not be a null pointer. Neithersmax
norn
shall be greater thanRSIZE_MAX
.n
shall not be greater thansmax
.
If there is a runtime-constraint violation, then if
s
is not a null pointer andsmax
is not greater thanRSIZE_MAX
, thememset_s
function stores the value ofc
(converted to anunsigned char
) into each of the firstsmax
characters of the object pointed to bys
.
Description: The
memset_s
function copies the value ofc
(converted to anunsigned char
) into each of the firstn
characters of the object pointed to bys
.
So I would naturally implement it something like this:
errno_t memset_s(void *s, rsize_t smax, int c, rsize_t n) {
bool violation = (s == NULL) || (smax > RSIZE_MAX) || (n > RSIZE_MAX) || (n > smax);
if (violation) {
if ((s != NULL) && !(smax > RSIZE_MAX)) {
for (rsize_t i = 0; i < smax; ++i) {
((volatile unsigned char*)s)[i] = c;
}
}
return EPERM;
} else {
for (rsize_t i = 0; i < n; ++i) {
((volatile unsigned char*)s)[i] = c;
}
return 0;
}
}
That seems to implement the specification 100% correctly, line for line.
You write the second for
-loop above as
i = 0;
while ( i < n )
{
*v_p++ = (unsigned char)c;
i++;
}
(yes, with two blank lines between the i = 0
and the rest of the loop). That's definitely too much code for a simple for
-loop. Even without doing anything else to your code, you could replace those 9 lines with 3 lines:
for (int i = 0; i < n; ++i) {
*v_p++ = (unsigned char)c;
}
A 66% reduction in lines-of-code is not bad for a day's work!
volatile unsigned char * v = s;
// ...
volatile unsigned char * v_p = &v[0];
You know that &v[0]
is the same thing as v
, right?
rsize_t i = 0;
if ( violation_present == 1 ) // && (s != NULL) && (smax <= RSIZE_MAX) )
{
i = 0;
Three things:
You initialize
i
to0
, and then again initialize it to0
. Are you worried that the first initialization might not have taken? :)The commented-out code is confusing. (I left a comment on the question referring to this and some other commented-out code; you did remove some of it, but left this snippet commented out.) If you meant for this code to take effect, you should uncomment it. If you meant for this code not to take effect, you should just delete it. Don't leave commented-out code hanging around. (If it's for historical interest, you should learn
git
or some other version-control system.)You branch on if
violation_present == 1
. This suggests thatviolation_present
might take on other values, such as0
(which it does) or2
or42
(which it does not). The compiler will likely generate code to compareviolation_present
against the constant1
. But actually all you mean here is "If there's a violation present...", which is idiomatically expressed asif (violation_present) ...
. Furthermore, you should look up thebool
type (defined in<stdbool.h>
) — it's tailor-made for boolean variables that can only ever take on the valuetrue
orfalse
. (Notice the use ofbool
in my reference implementation above.)
fprintf(stderr,"memset_s: Error: void * s == NULL!n");
Missing whitespace again.
Here you have a memset_s
function, whose job is to set a range of bytes to a value... and you've got it pulling in fprintf
from the standard library! Does that seem appropriate to you?
memset_s
is a very low-level function. It should be usable even on embedded systems that have never heard of fprintf
or stderr
. You should find a way to report errors that doesn't involve <stdio.h>
. (Might I suggest errno_t
? :))
$endgroup$
add a comment |
$begingroup$
errno_t memset_s(void *s,rsize_t smax, int c, rsize_t n)
You've got a missing space after that first comma. (Sure, inconsistent whitespace doesn't affect functionality; but like any spelling mistake, it indicates that you haven't proofread your code. Which means it probably has some other one-character typos too. Some of which might affect functionality. Always read your code after writing it!)
The typedef errno_t
is invariably int
. So personally I'd just write int
— unless you were planning to use the association with errno
in some significant way. Right now your code just returns 0
on success and 1
(which is to say EPERM
) on failure. Consider whether memset_s
should return a more specific and helpful errno than simply EPERM
. For example, maybe if you pass NULL to it, you should get back EINVAL
"Invalid argument"?
However, at the same time, YAGNI — it may be counterproductive to spend a lot of time agonizing over what are the proper errno return values, unless you have an actual use-case for these return values. The only reason to return EINVAL
for a null argument instead of the generic EPERM
is that it enables a caller to detect that specific error case and handle the error accordingly. But the caller can already detect that case! The caller doesn't need your help to detect s == NULL
! The caller controls the value of s
in the first place, and can easily check for null beforehand, if they think it's possible for s
to be null. (And if it's not possible for s
to be null, then there's no point checking inside memset_s
either. That's just a waste of CPU cycles.)
Can you tell that I think memset_s
is largely a waste of time, design-wise? :)
I find your chain of if
s difficult to match up to the specification. The specification is as follows:
Runtime-constraints:
s
shall not be a null pointer. Neithersmax
norn
shall be greater thanRSIZE_MAX
.n
shall not be greater thansmax
.
If there is a runtime-constraint violation, then if
s
is not a null pointer andsmax
is not greater thanRSIZE_MAX
, thememset_s
function stores the value ofc
(converted to anunsigned char
) into each of the firstsmax
characters of the object pointed to bys
.
Description: The
memset_s
function copies the value ofc
(converted to anunsigned char
) into each of the firstn
characters of the object pointed to bys
.
So I would naturally implement it something like this:
errno_t memset_s(void *s, rsize_t smax, int c, rsize_t n) {
bool violation = (s == NULL) || (smax > RSIZE_MAX) || (n > RSIZE_MAX) || (n > smax);
if (violation) {
if ((s != NULL) && !(smax > RSIZE_MAX)) {
for (rsize_t i = 0; i < smax; ++i) {
((volatile unsigned char*)s)[i] = c;
}
}
return EPERM;
} else {
for (rsize_t i = 0; i < n; ++i) {
((volatile unsigned char*)s)[i] = c;
}
return 0;
}
}
That seems to implement the specification 100% correctly, line for line.
You write the second for
-loop above as
i = 0;
while ( i < n )
{
*v_p++ = (unsigned char)c;
i++;
}
(yes, with two blank lines between the i = 0
and the rest of the loop). That's definitely too much code for a simple for
-loop. Even without doing anything else to your code, you could replace those 9 lines with 3 lines:
for (int i = 0; i < n; ++i) {
*v_p++ = (unsigned char)c;
}
A 66% reduction in lines-of-code is not bad for a day's work!
volatile unsigned char * v = s;
// ...
volatile unsigned char * v_p = &v[0];
You know that &v[0]
is the same thing as v
, right?
rsize_t i = 0;
if ( violation_present == 1 ) // && (s != NULL) && (smax <= RSIZE_MAX) )
{
i = 0;
Three things:
You initialize
i
to0
, and then again initialize it to0
. Are you worried that the first initialization might not have taken? :)The commented-out code is confusing. (I left a comment on the question referring to this and some other commented-out code; you did remove some of it, but left this snippet commented out.) If you meant for this code to take effect, you should uncomment it. If you meant for this code not to take effect, you should just delete it. Don't leave commented-out code hanging around. (If it's for historical interest, you should learn
git
or some other version-control system.)You branch on if
violation_present == 1
. This suggests thatviolation_present
might take on other values, such as0
(which it does) or2
or42
(which it does not). The compiler will likely generate code to compareviolation_present
against the constant1
. But actually all you mean here is "If there's a violation present...", which is idiomatically expressed asif (violation_present) ...
. Furthermore, you should look up thebool
type (defined in<stdbool.h>
) — it's tailor-made for boolean variables that can only ever take on the valuetrue
orfalse
. (Notice the use ofbool
in my reference implementation above.)
fprintf(stderr,"memset_s: Error: void * s == NULL!n");
Missing whitespace again.
Here you have a memset_s
function, whose job is to set a range of bytes to a value... and you've got it pulling in fprintf
from the standard library! Does that seem appropriate to you?
memset_s
is a very low-level function. It should be usable even on embedded systems that have never heard of fprintf
or stderr
. You should find a way to report errors that doesn't involve <stdio.h>
. (Might I suggest errno_t
? :))
$endgroup$
errno_t memset_s(void *s,rsize_t smax, int c, rsize_t n)
You've got a missing space after that first comma. (Sure, inconsistent whitespace doesn't affect functionality; but like any spelling mistake, it indicates that you haven't proofread your code. Which means it probably has some other one-character typos too. Some of which might affect functionality. Always read your code after writing it!)
The typedef errno_t
is invariably int
. So personally I'd just write int
— unless you were planning to use the association with errno
in some significant way. Right now your code just returns 0
on success and 1
(which is to say EPERM
) on failure. Consider whether memset_s
should return a more specific and helpful errno than simply EPERM
. For example, maybe if you pass NULL to it, you should get back EINVAL
"Invalid argument"?
However, at the same time, YAGNI — it may be counterproductive to spend a lot of time agonizing over what are the proper errno return values, unless you have an actual use-case for these return values. The only reason to return EINVAL
for a null argument instead of the generic EPERM
is that it enables a caller to detect that specific error case and handle the error accordingly. But the caller can already detect that case! The caller doesn't need your help to detect s == NULL
! The caller controls the value of s
in the first place, and can easily check for null beforehand, if they think it's possible for s
to be null. (And if it's not possible for s
to be null, then there's no point checking inside memset_s
either. That's just a waste of CPU cycles.)
Can you tell that I think memset_s
is largely a waste of time, design-wise? :)
I find your chain of if
s difficult to match up to the specification. The specification is as follows:
Runtime-constraints:
s
shall not be a null pointer. Neithersmax
norn
shall be greater thanRSIZE_MAX
.n
shall not be greater thansmax
.
If there is a runtime-constraint violation, then if
s
is not a null pointer andsmax
is not greater thanRSIZE_MAX
, thememset_s
function stores the value ofc
(converted to anunsigned char
) into each of the firstsmax
characters of the object pointed to bys
.
Description: The
memset_s
function copies the value ofc
(converted to anunsigned char
) into each of the firstn
characters of the object pointed to bys
.
So I would naturally implement it something like this:
errno_t memset_s(void *s, rsize_t smax, int c, rsize_t n) {
bool violation = (s == NULL) || (smax > RSIZE_MAX) || (n > RSIZE_MAX) || (n > smax);
if (violation) {
if ((s != NULL) && !(smax > RSIZE_MAX)) {
for (rsize_t i = 0; i < smax; ++i) {
((volatile unsigned char*)s)[i] = c;
}
}
return EPERM;
} else {
for (rsize_t i = 0; i < n; ++i) {
((volatile unsigned char*)s)[i] = c;
}
return 0;
}
}
That seems to implement the specification 100% correctly, line for line.
You write the second for
-loop above as
i = 0;
while ( i < n )
{
*v_p++ = (unsigned char)c;
i++;
}
(yes, with two blank lines between the i = 0
and the rest of the loop). That's definitely too much code for a simple for
-loop. Even without doing anything else to your code, you could replace those 9 lines with 3 lines:
for (int i = 0; i < n; ++i) {
*v_p++ = (unsigned char)c;
}
A 66% reduction in lines-of-code is not bad for a day's work!
volatile unsigned char * v = s;
// ...
volatile unsigned char * v_p = &v[0];
You know that &v[0]
is the same thing as v
, right?
rsize_t i = 0;
if ( violation_present == 1 ) // && (s != NULL) && (smax <= RSIZE_MAX) )
{
i = 0;
Three things:
You initialize
i
to0
, and then again initialize it to0
. Are you worried that the first initialization might not have taken? :)The commented-out code is confusing. (I left a comment on the question referring to this and some other commented-out code; you did remove some of it, but left this snippet commented out.) If you meant for this code to take effect, you should uncomment it. If you meant for this code not to take effect, you should just delete it. Don't leave commented-out code hanging around. (If it's for historical interest, you should learn
git
or some other version-control system.)You branch on if
violation_present == 1
. This suggests thatviolation_present
might take on other values, such as0
(which it does) or2
or42
(which it does not). The compiler will likely generate code to compareviolation_present
against the constant1
. But actually all you mean here is "If there's a violation present...", which is idiomatically expressed asif (violation_present) ...
. Furthermore, you should look up thebool
type (defined in<stdbool.h>
) — it's tailor-made for boolean variables that can only ever take on the valuetrue
orfalse
. (Notice the use ofbool
in my reference implementation above.)
fprintf(stderr,"memset_s: Error: void * s == NULL!n");
Missing whitespace again.
Here you have a memset_s
function, whose job is to set a range of bytes to a value... and you've got it pulling in fprintf
from the standard library! Does that seem appropriate to you?
memset_s
is a very low-level function. It should be usable even on embedded systems that have never heard of fprintf
or stderr
. You should find a way to report errors that doesn't involve <stdio.h>
. (Might I suggest errno_t
? :))
answered 2 mins ago
QuuxplusoneQuuxplusone
12.5k12061
12.5k12061
add a comment |
add a comment |
Tanveer Salim is a new contributor. Be nice, and check out our Code of Conduct.
Tanveer Salim is a new contributor. Be nice, and check out our Code of Conduct.
Tanveer Salim is a new contributor. Be nice, and check out our Code of Conduct.
Tanveer Salim is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f215309%2fimplementation-of-memset-s-based-on-c11-standard%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
$begingroup$
You have a bunch of commented-out code in there. Please either uncomment it (if it's meant to be part of the code under review) or delete it (if it's meant not to be part of the code under review).
$endgroup$
– Quuxplusone
2 hours ago
$begingroup$
Thanks Quuxplusone. Sure.
$endgroup$
– Tanveer Salim
1 hour ago