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