blob: 996cf3a9071e0a9a2b2878f1dcfb676ea11e236e [file] [log] [blame]
Laurence Lundblade41e96ca2022-04-09 10:37:39 -06001/* =========================================================================
2 ub-example.c -- Example code for UsefulBuf
3
4 Copyright (c) 2022, Laurence Lundblade. All rights reserved.
5
6 SPDX-License-Identifier: BSD-3-Clause
7
8 See BSD-3-Clause license in README.md
9
10 Created on 4/8/22
11 ========================================================================== */
12
13#include "ub-example.h"
14
15#include "UsefulBuf.h"
16
17
18/*
19 * A considerable number of the security issues with C code come from
20 * mistakes made with pointers and lengths. UsefulBuf adopts a
21 * convention that a pointer and length *always* go together to help
22 * mitigate this. With UsefulBuf there are never pointers without
23 * lengths, so you always know how big a buffer or some binary data
24 * is.
25 *
26 * C99 allows passing structures so a structure is used. Compilers are
27 * smart these days so the object code produced is little different
28 * than passing two separate parameters. Passing structures also makes
29 * the interfaces prettier. Assignments of structures also can make
30 * code prettier.
31 *
32 * ALong with the UsefulBuf structure, there are a bunch of (tested!)
33 * functions to manipulate them so code using it may have no pointer
34 * manipulation at all.
35 *
36 * Constness is also a useful and desirous thing. See
37 * https://stackoverflow.com/questions/117293/use-of-const-for-function-parameters
38 * Keeping const distinct from non-const is helpful when reading the
39 * code and helps avoid some coding mistakes. In this example the
40 * buffers filled in with data are const and the ones that are
41 * to-be-filled in are not const.
42 *
43 * This contrived example copies data from input to output expanding
44 * bytes with the value 'x' to 'xx'.
45 *
46 * Input -- This is the pointer and length of the input, the bytes to
47 * copy. Note that UsefulBufC.ptr is a const void * indicating that
48 * input data won't be changed by this function. There is a "C" in
49 * "UsefulBufC "to indicate the value is const. The length here is
50 * the length of the valid input data. Note also that the parameter
51 * Input is const, so this is fully const and clearly an [in]
52 * parameter.
53 *
54 * OutputBuffer -- This is a pointer and length of the memory to be
55 * used to store the output. The correct length here is critical for
56 * code security. Note that UsefulBuf.ptr is void *, it is not const
57 * indicating data can be written to it. Note that the parameter
58 * itself *is* const indicating that the code below will not point
59 * this to some other buffer or change the length and clearly marking
60 * it as an [in] parameter.
61 *
62 * Output -- This is the interesting and unusual one. To stay
63 * consistent with always pairing a length and a pointer, this is
64 * returned as a UsefulBuC. Also, to stay consistent with valid data
65 * being const, it is a UsefulBufC, not a UsefulBuf. It is however, an
66 * [out] parameter so the parameter is a pointer to a UsefulBufC.
67 *
68 * In this case and most cases, the pointer in Output->ptr will be the
69 * same as OutputBuffer.ptr. This may seem redundant, but there are a
70 * few reasons for it. First, is the goal of always pairing a pointer
71 * and a length. Second is being more strict and correct with
72 * constness. Third is the code hygiene and clarity of having
73 * variables for to-be-filled buffers be distinct from those
74 * containing valid data. Fourth, there are no [in,out] parameters,
75 * only [in] parameters and [out] parameters (the to-be-filled-in
76 * buffer is considered an [in] parameter).
77 *
78 * Note that the compiler will be smart and should generate pretty
79 * much the same code as for a traditional interface. On x86 with
80 * gcc-11 and no stack guards, the UB code is 81 bytes and the
81 * traditional code is 77 bytes.
82 *
83 * Finally, this supports computing of the length of the would-be
84 * output without actually doing any outputting. Pass {NULL, SIZE_MAX}
85 * for the OutputBuffer and the length will be returned in Output.
86 */
87int
88ExpandxUB(const UsefulBufC Input,
89 const UsefulBuf OutputBuffer,
90 UsefulBufC *Output)
91{
Laurence Lundblade98c363d2022-05-15 17:21:31 -070092 size_t uInputPosition;
93 size_t uOutputPosition;
Laurence Lundblade41e96ca2022-04-09 10:37:39 -060094
Laurence Lundblade98c363d2022-05-15 17:21:31 -070095 uOutputPosition = 0;
Laurence Lundblade41e96ca2022-04-09 10:37:39 -060096
97 /* Loop over all the bytes in Input */
Laurence Lundblade98c363d2022-05-15 17:21:31 -070098 for(uInputPosition = 0; uInputPosition < Input.len; uInputPosition++) {
99 const uint8_t uInputByte = ((const uint8_t*)Input.ptr)[uInputPosition];
Laurence Lundblade41e96ca2022-04-09 10:37:39 -0600100
101 /* Copy every byte */
102 if(OutputBuffer.ptr != NULL) {
Laurence Lundblade98c363d2022-05-15 17:21:31 -0700103 ((uint8_t *)OutputBuffer.ptr)[uOutputPosition] = uInputByte;
Laurence Lundblade41e96ca2022-04-09 10:37:39 -0600104 }
Laurence Lundblade98c363d2022-05-15 17:21:31 -0700105 uOutputPosition++;
106 if(uOutputPosition >= OutputBuffer.len) {
Laurence Lundblade41e96ca2022-04-09 10:37:39 -0600107 return -1;
108 }
109
110 /* Double output 'x' because that is what this contrived example does */
Laurence Lundblade98c363d2022-05-15 17:21:31 -0700111 if(uInputByte== 'x') {
Laurence Lundblade41e96ca2022-04-09 10:37:39 -0600112 if(OutputBuffer.ptr != NULL) {
Laurence Lundblade98c363d2022-05-15 17:21:31 -0700113 ((uint8_t *)OutputBuffer.ptr)[uOutputPosition] = 'x';
Laurence Lundblade41e96ca2022-04-09 10:37:39 -0600114 }
Laurence Lundblade98c363d2022-05-15 17:21:31 -0700115 uOutputPosition++;
116 if(uOutputPosition >= OutputBuffer.len) {
Laurence Lundblade41e96ca2022-04-09 10:37:39 -0600117 return -1;
118 }
119 }
120 }
121
Laurence Lundblade98c363d2022-05-15 17:21:31 -0700122 *Output = (UsefulBufC){OutputBuffer.ptr, uOutputPosition};
Laurence Lundblade41e96ca2022-04-09 10:37:39 -0600123
124 return 0; /* success */
125}
126
127
128/* This is the more tradional way to implement this. */
129int
130ExpandxTraditional(const uint8_t *pInputPointer,
131 const size_t uInputLength,
132 uint8_t *pOutputBuffer,
133 const size_t uOutputBufferLength,
134 size_t *puOutputLength)
135{
Laurence Lundblade98c363d2022-05-15 17:21:31 -0700136 size_t uInputPosition;
137 size_t uOutputPosition;
Laurence Lundblade41e96ca2022-04-09 10:37:39 -0600138
Laurence Lundblade98c363d2022-05-15 17:21:31 -0700139 uOutputPosition = 0;
Laurence Lundblade41e96ca2022-04-09 10:37:39 -0600140
141 /* Loop over all the bytes in Input */
Laurence Lundblade98c363d2022-05-15 17:21:31 -0700142 for(uInputPosition = 0; uInputPosition < uInputLength; uInputPosition++) {
143 const uint8_t uInputByte = ((const uint8_t*)pInputPointer)[uInputPosition];
Laurence Lundblade41e96ca2022-04-09 10:37:39 -0600144
145 /* Copy every byte */
146 if(pOutputBuffer != NULL) {
Laurence Lundblade98c363d2022-05-15 17:21:31 -0700147 ((uint8_t *)pOutputBuffer)[uOutputPosition] = uInputByte;
Laurence Lundblade41e96ca2022-04-09 10:37:39 -0600148 }
Laurence Lundblade98c363d2022-05-15 17:21:31 -0700149 uOutputPosition++;
150 if(uOutputPosition >= uOutputBufferLength) {
Laurence Lundblade41e96ca2022-04-09 10:37:39 -0600151 return -1;
152 }
153
154 /* Double output 'x' because that is what this contrived example does */
Laurence Lundblade98c363d2022-05-15 17:21:31 -0700155 if(uInputByte== 'x') {
Laurence Lundblade41e96ca2022-04-09 10:37:39 -0600156 if(pOutputBuffer != NULL) {
Laurence Lundblade98c363d2022-05-15 17:21:31 -0700157 ((uint8_t *)pOutputBuffer)[uOutputPosition] = 'x';
Laurence Lundblade41e96ca2022-04-09 10:37:39 -0600158 }
Laurence Lundblade98c363d2022-05-15 17:21:31 -0700159 uOutputPosition++;
160 if(uOutputPosition >= uOutputBufferLength) {
Laurence Lundblade41e96ca2022-04-09 10:37:39 -0600161 return -1;
162 }
163 }
164 }
165
Laurence Lundblade98c363d2022-05-15 17:21:31 -0700166 *puOutputLength = uOutputPosition;
Laurence Lundblade41e96ca2022-04-09 10:37:39 -0600167
168 return 0; /* success */
169}
170
171
172/*
173 * Here's an example of going from a traditional interface
174 * interface to a UsefulBuf interface.
175 */
176int
177ExpandxTraditionalAdaptor(const uint8_t *pInputPointer,
178 size_t uInputLength,
179 uint8_t *pOutputBuffer,
180 size_t uOutputBufferLength,
181 size_t *puOutputLength)
182{
183 UsefulBufC Input;
184 UsefulBuf OutputBuffer;
185 UsefulBufC Output;
186 int nReturn;
187
188 Input = (UsefulBufC){pInputPointer, uInputLength};
189 OutputBuffer = (UsefulBuf){pOutputBuffer, uOutputBufferLength};
190
191 nReturn = ExpandxUB(Input, OutputBuffer, &Output);
192
193 *puOutputLength = Output.len;
194
195 return nReturn;
196}
197
198
199/* Here's an example for going from a UsefulBuf interface
200 to a traditional interface. */
201int
202ExpandxUBAdaptor(const UsefulBufC Input,
203 const UsefulBuf OutputBuffer,
204 UsefulBufC *Output)
205{
206 Output->ptr = OutputBuffer.ptr;
207
208 return ExpandxTraditional(Input.ptr, Input.len,
209 OutputBuffer.ptr, OutputBuffer.len,
210 &(Output->len));
211}
212
213
214
215#define INPUT "xyz123xyz"
216
Maxim Zhukovd538f0a2022-12-20 20:40:38 +0300217int32_t RunUsefulBufExample(void)
Laurence Lundblade41e96ca2022-04-09 10:37:39 -0600218{
219 /* ------------ UsefulBuf examples ------------- */
220 UsefulBufC Input = UsefulBuf_FROM_SZ_LITERAL(INPUT);
221
222 /* This macros makes a 20 byte buffer on the stack. It also makes
223 * a UsefulBuf on the stack. It sets up the UsefulBuf to point to
224 * the 20 byte buffer and sets it's length to 20 bytes. This
225 * is the empty, to-be-filled in memory for the output. It is not
226 * const. */
227 MakeUsefulBufOnStack(OutBuf, sizeof(INPUT) * 2);
228
229 /* This is were the pointer and the length of the completed output
230 * will be placed. Output.ptr is a pointer to const bytes. */
231 UsefulBufC Output;
232
233 ExpandxUB(Input, OutBuf, &Output);
234
235 ExpandxUBAdaptor(Input, OutBuf, &Output);
236
237
238
239 /* ------ Get Size example -------- */
240 ExpandxUB(Input, (UsefulBuf){NULL, SIZE_MAX}, &Output);
241
242 /* Size is in Output.len */
243
244
245
246 /* ---------- Traditional examples (for comparison) --------- */
247 uint8_t puBuffer[sizeof(INPUT) * 2];
248 size_t uOutputSize;
249
250 ExpandxTraditional((const uint8_t *)INPUT, sizeof(INPUT),
251 puBuffer, sizeof(puBuffer),
252 &uOutputSize);
253
254
255 ExpandxTraditionalAdaptor((const uint8_t *)INPUT, sizeof(INPUT),
256 puBuffer, sizeof(puBuffer),
257 &uOutputSize);
258
259 return 0;
260}