1 1.5 christos /* $NetBSD: option_unittest.c,v 1.5 2022/10/05 22:20:15 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* 4 1.4 christos * Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC") 5 1.1 christos * 6 1.1 christos * This Source Code Form is subject to the terms of the Mozilla Public 7 1.1 christos * License, v. 2.0. If a copy of the MPL was not distributed with this 8 1.1 christos * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 1.1 christos * 10 1.1 christos * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 11 1.1 christos * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 12 1.1 christos * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 13 1.1 christos * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 1.1 christos * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 15 1.1 christos * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 1.1 christos * PERFORMANCE OF THIS SOFTWARE. 17 1.1 christos */ 18 1.1 christos 19 1.1 christos #include <config.h> 20 1.1 christos #include <atf-c.h> 21 1.1 christos #include "dhcpd.h" 22 1.1 christos 23 1.1 christos ATF_TC(option_refcnt); 24 1.1 christos 25 1.1 christos ATF_TC_HEAD(option_refcnt, tc) 26 1.1 christos { 27 1.1 christos atf_tc_set_md_var(tc, "descr", 28 1.1 christos "Verify option reference count does not overflow."); 29 1.1 christos } 30 1.1 christos 31 1.1 christos /* This test does a simple check to see if option reference count is 32 1.1 christos * decremented even an error path exiting parse_option_buffer() 33 1.1 christos */ 34 1.1 christos ATF_TC_BODY(option_refcnt, tc) 35 1.1 christos { 36 1.1 christos struct option_state *options; 37 1.1 christos struct option *option; 38 1.1 christos unsigned code; 39 1.1 christos int refcnt; 40 1.1 christos unsigned char buffer[3] = { 15, 255, 0 }; 41 1.1 christos 42 1.1 christos initialize_common_option_spaces(); 43 1.1 christos 44 1.1 christos options = NULL; 45 1.1 christos if (!option_state_allocate(&options, MDL)) { 46 1.1 christos atf_tc_fail("can't allocate option state"); 47 1.1 christos } 48 1.1 christos 49 1.1 christos option = NULL; 50 1.1 christos code = 15; /* domain-name */ 51 1.1 christos if (!option_code_hash_lookup(&option, dhcp_universe.code_hash, 52 1.1 christos &code, 0, MDL)) { 53 1.1 christos atf_tc_fail("can't find option 15"); 54 1.1 christos } 55 1.1 christos if (option == NULL) { 56 1.1 christos atf_tc_fail("option is NULL"); 57 1.1 christos } 58 1.1 christos refcnt = option->refcnt; 59 1.1 christos 60 1.1 christos buffer[0] = 15; 61 1.1 christos buffer[1] = 255; /* invalid */ 62 1.1 christos buffer[2] = 0; 63 1.1 christos 64 1.1 christos if (parse_option_buffer(options, buffer, 3, &dhcp_universe)) { 65 1.1 christos atf_tc_fail("parse_option_buffer is expected to fail"); 66 1.1 christos } 67 1.1 christos 68 1.1 christos if (refcnt != option->refcnt) { 69 1.1 christos atf_tc_fail("refcnt changed from %d to %d", refcnt, option->refcnt); 70 1.1 christos } 71 1.1 christos } 72 1.1 christos 73 1.1 christos ATF_TC(pretty_print_option); 74 1.1 christos 75 1.1 christos ATF_TC_HEAD(pretty_print_option, tc) 76 1.1 christos { 77 1.1 christos atf_tc_set_md_var(tc, "descr", 78 1.1 christos "Verify pretty_print_option does not overrun its buffer."); 79 1.1 christos } 80 1.1 christos 81 1.1 christos 82 1.1 christos /* 83 1.1 christos * This test verifies that pretty_print_option() will not overrun its 84 1.1 christos * internal, static buffer when given large 'x/X' format options. 85 1.1 christos * 86 1.1 christos */ 87 1.1 christos ATF_TC_BODY(pretty_print_option, tc) 88 1.1 christos { 89 1.1 christos struct option *option; 90 1.1 christos unsigned code; 91 1.1 christos unsigned char bad_data[32*1024]; 92 1.1 christos unsigned char good_data[] = { 1,2,3,4,5,6 }; 93 1.1 christos int emit_commas = 1; 94 1.1 christos int emit_quotes = 1; 95 1.1 christos const char *output_buf; 96 1.1 christos 97 1.1 christos /* Initialize whole thing to non-printable chars */ 98 1.1 christos memset(bad_data, 0x1f, sizeof(bad_data)); 99 1.1 christos 100 1.1 christos initialize_common_option_spaces(); 101 1.1 christos 102 1.1 christos /* We'll use dhcp_client_identitifer because it happens to be format X */ 103 1.1 christos code = 61; 104 1.1 christos option = NULL; 105 1.1 christos if (!option_code_hash_lookup(&option, dhcp_universe.code_hash, 106 1.1 christos &code, 0, MDL)) { 107 1.1 christos atf_tc_fail("can't find option %d", code); 108 1.1 christos } 109 1.1 christos 110 1.1 christos if (option == NULL) { 111 1.1 christos atf_tc_fail("option is NULL"); 112 1.1 christos } 113 1.1 christos 114 1.1 christos /* First we will try a good value we know should fit. */ 115 1.1 christos output_buf = pretty_print_option (option, good_data, sizeof(good_data), 116 1.1 christos emit_commas, emit_quotes); 117 1.1 christos 118 1.1 christos /* Make sure we get what we expect */ 119 1.1 christos if (!output_buf || strcmp(output_buf, "1:2:3:4:5:6")) { 120 1.1 christos atf_tc_fail("pretty_print_option did not return \"<error>\""); 121 1.1 christos } 122 1.1 christos 123 1.1 christos 124 1.1 christos /* Now we'll try a data value that's too large */ 125 1.1 christos output_buf = pretty_print_option (option, bad_data, sizeof(bad_data), 126 1.1 christos emit_commas, emit_quotes); 127 1.1 christos 128 1.1 christos /* Make sure we safely get an error */ 129 1.1 christos if (!output_buf || strcmp(output_buf, "<error>")) { 130 1.1 christos atf_tc_fail("pretty_print_option did not return \"<error>\""); 131 1.1 christos } 132 1.1 christos } 133 1.1 christos 134 1.3 christos ATF_TC(parse_X); 135 1.3 christos 136 1.3 christos ATF_TC_HEAD(parse_X, tc) 137 1.3 christos { 138 1.3 christos atf_tc_set_md_var(tc, "descr", 139 1.4 christos "Verify parse_X services option too big."); 140 1.3 christos } 141 1.3 christos 142 1.3 christos /* Initializes a parse struct from an input buffer of data. */ 143 1.3 christos static void init_parse(struct parse *cfile, char* name, char *input) { 144 1.3 christos memset(cfile, 0, sizeof(struct parse)); 145 1.3 christos cfile->tlname = name; 146 1.3 christos cfile->lpos = cfile->line = 1; 147 1.3 christos cfile->cur_line = cfile->line1; 148 1.3 christos cfile->prev_line = cfile->line2; 149 1.3 christos cfile->token_line = cfile->cur_line; 150 1.3 christos cfile->cur_line[0] = cfile->prev_line[0] = 0; 151 1.3 christos cfile->file = -1; 152 1.3 christos cfile->eol_token = 0; 153 1.3 christos 154 1.3 christos cfile->inbuf = input; 155 1.3 christos cfile->buflen = strlen(input); 156 1.3 christos cfile->bufsiz = 0; 157 1.3 christos } 158 1.3 christos 159 1.3 christos /* 160 1.3 christos * This test verifies that parse_X does not overwrite the output 161 1.3 christos * buffer when given input data that exceeds the output buffer 162 1.3 christos * capacity. 163 1.3 christos */ 164 1.3 christos ATF_TC_BODY(parse_X, tc) 165 1.3 christos { 166 1.3 christos struct parse cfile; 167 1.3 christos u_int8_t output[10]; 168 1.3 christos unsigned len; 169 1.3 christos 170 1.3 christos /* Input hex literal */ 171 1.3 christos char *input = "01:02:03:04:05:06:07:08"; 172 1.3 christos unsigned expected_len = 8; 173 1.3 christos 174 1.3 christos /* Normal output plus two filler bytes */ 175 1.3 christos u_int8_t expected_plus_two[] = { 176 1.3 christos 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0xff, 0xff 177 1.3 christos }; 178 1.3 christos 179 1.3 christos /* Safe output when option is too long */ 180 1.3 christos unsigned short_buf_len = 4; 181 1.3 christos u_int8_t expected_too_long[] = { 182 1.3 christos 0x01, 0x02, 0x03, 0x04, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 183 1.3 christos }; 184 1.3 christos 185 1.3 christos /* First we'll run one that works normally */ 186 1.3 christos memset(output, 0xff, sizeof(output)); 187 1.3 christos init_parse(&cfile, "hex_fits", input); 188 1.3 christos 189 1.3 christos len = parse_X(&cfile, output, expected_len); 190 1.3 christos 191 1.3 christos // Len should match the expected len. 192 1.3 christos if (len != expected_len) { 193 1.3 christos atf_tc_fail("parse_X failed, output len: %d", len); 194 1.3 christos } 195 1.3 christos 196 1.3 christos // We should not have written anything past the end of the buffer. 197 1.3 christos if (memcmp(output, expected_plus_two, sizeof(output))) { 198 1.3 christos atf_tc_fail("parse_X failed, output does not match expected"); 199 1.3 christos } 200 1.3 christos 201 1.3 christos // Now we'll try it with a buffer that's too small. 202 1.3 christos init_parse(&cfile, "hex_too_long", input); 203 1.3 christos memset(output, 0xff, sizeof(output)); 204 1.3 christos 205 1.3 christos len = parse_X(&cfile, output, short_buf_len); 206 1.3 christos 207 1.3 christos // On errors, len should be zero. 208 1.3 christos if (len != 0) { 209 1.3 christos atf_tc_fail("parse_X failed, we should have had an error"); 210 1.3 christos } 211 1.3 christos 212 1.3 christos // We should not have written anything past the end of the buffer. 213 1.3 christos if (memcmp(output, expected_too_long, sizeof(output))) { 214 1.3 christos atf_tc_fail("parse_X overwrote buffer!"); 215 1.3 christos } 216 1.3 christos } 217 1.1 christos 218 1.5 christos ATF_TC(add_option_ref_cnt); 219 1.5 christos 220 1.5 christos ATF_TC_HEAD(add_option_ref_cnt, tc) 221 1.5 christos { 222 1.5 christos atf_tc_set_md_var(tc, "descr", 223 1.5 christos "Verify add_option() does not leak option ref counts."); 224 1.5 christos } 225 1.5 christos 226 1.5 christos ATF_TC_BODY(add_option_ref_cnt, tc) 227 1.5 christos { 228 1.5 christos struct option_state *options = NULL; 229 1.5 christos struct option *option = NULL; 230 1.5 christos unsigned int cid_code = DHO_DHCP_CLIENT_IDENTIFIER; 231 1.5 christos char *cid_str = "1234"; 232 1.5 christos int refcnt_before = 0; 233 1.5 christos 234 1.5 christos // Look up the option we're going to add. 235 1.5 christos initialize_common_option_spaces(); 236 1.5 christos if (!option_code_hash_lookup(&option, dhcp_universe.code_hash, 237 1.5 christos &cid_code, 0, MDL)) { 238 1.5 christos atf_tc_fail("cannot find option definition?"); 239 1.5 christos } 240 1.5 christos 241 1.5 christos // Get the option's reference count before we call add_options. 242 1.5 christos refcnt_before = option->refcnt; 243 1.5 christos 244 1.5 christos // Allocate a option_state to which to add an option. 245 1.5 christos if (!option_state_allocate(&options, MDL)) { 246 1.5 christos atf_tc_fail("cannot allocat options state"); 247 1.5 christos } 248 1.5 christos 249 1.5 christos // Call add_option() to add the option to the option state. 250 1.5 christos if (!add_option(options, cid_code, cid_str, strlen(cid_str))) { 251 1.5 christos atf_tc_fail("add_option returned 0"); 252 1.5 christos } 253 1.5 christos 254 1.5 christos // Verify that calling add_option() only adds 1 to the option ref count. 255 1.5 christos if (option->refcnt != (refcnt_before + 1)) { 256 1.5 christos atf_tc_fail("after add_option(), count is wrong, before %d, after: %d", 257 1.5 christos refcnt_before, option->refcnt); 258 1.5 christos } 259 1.5 christos 260 1.5 christos // Derefrence the option_state, this should reduce the ref count to 261 1.5 christos // it's starting value. 262 1.5 christos option_state_dereference(&options, MDL); 263 1.5 christos 264 1.5 christos // Verify that dereferencing option_state restores option ref count. 265 1.5 christos if (option->refcnt != refcnt_before) { 266 1.5 christos atf_tc_fail("after state deref, count is wrong, before %d, after: %d", 267 1.5 christos refcnt_before, option->refcnt); 268 1.5 christos } 269 1.5 christos } 270 1.5 christos 271 1.1 christos /* This macro defines main() method that will call specified 272 1.1 christos test cases. tp and simple_test_case names can be whatever you want 273 1.1 christos as long as it is a valid variable identifier. */ 274 1.1 christos ATF_TP_ADD_TCS(tp) 275 1.1 christos { 276 1.1 christos ATF_TP_ADD_TC(tp, option_refcnt); 277 1.1 christos ATF_TP_ADD_TC(tp, pretty_print_option); 278 1.3 christos ATF_TP_ADD_TC(tp, parse_X); 279 1.5 christos ATF_TP_ADD_TC(tp, add_option_ref_cnt); 280 1.1 christos 281 1.1 christos return (atf_no_error()); 282 1.1 christos } 283