Path: blob/main/crypto/krb5/src/util/profile/t_profile.c
34889 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* util/profile/t_profile.c - profile library regression tests */2/*3* Copyright (C) 2021 by the Massachusetts Institute of Technology.4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9*10* * Redistributions of source code must retain the above copyright11* notice, this list of conditions and the following disclaimer.12*13* * Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in15* the documentation and/or other materials provided with the16* distribution.17*18* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS19* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT20* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS21* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE22* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,23* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES24* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR25* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)26* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,27* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)28* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED29* OF THE POSSIBILITY OF SUCH DAMAGE.30*/3132#include <assert.h>33#include <stdarg.h>34#include <stdio.h>35#include <stdlib.h>36#include <string.h>37#include <time.h>38#include <unistd.h>39#include <utime.h>40#include "profile.h"4142static void43check(long code)44{45assert(code == 0);46}4748static void49check_fail(long code, long expected)50{51assert(code == expected);52}5354static void55write_file(const char *name, int nlines, ...)56{57FILE *f;58va_list ap;59int i;6061(void)unlink(name);62f = fopen(name, "w");63assert(f != NULL);64va_start(ap, nlines);65for (i = 0; i < nlines; i++)66fprintf(f, "%s\n", va_arg(ap, char *));67va_end(ap);68fclose(f);69}7071/* Regression test for #2685 (profile iterator breaks when modifications72* made) */73static void74test_iterate(void)75{76profile_t p;77void *iter;78const char *names[] = { "test section 1", "child_section", "child", NULL };79const char *values[] = { "slick", "harry", "john", NULL };80char *name, *value;81int i;8283check(profile_init_path("test2.ini", &p));8485/* Iterate and check for the expected values. */86check(profile_iterator_create(p, names, 0, &iter));87for (i = 0;; i++) {88check(profile_iterator(&iter, &name, &value));89if (name == NULL && value == NULL)90break;91assert(strcmp(name, names[2]) == 0);92assert(values[i] != NULL);93assert(strcmp(value, values[i]) == 0);94profile_release_string(name);95profile_release_string(value);96}97assert(values[i] == NULL);98profile_iterator_free(&iter);99100/* Iterate again, deleting each value as we go. Flush the result to a101* separate file. */102check(profile_iterator_create(p, names, 0, &iter));103for (;;) {104check(profile_iterator(&iter, NULL, &value));105if (value == NULL)106break;107check(profile_update_relation(p, names, value, NULL));108profile_release_string(value);109}110profile_iterator_free(&iter);111(void)unlink("test3.ini");112profile_flush_to_file(p, "test3.ini");113114profile_abandon(p);115116/* Check that no values for the section are found in the resulting file. */117check(profile_init_path("test3.ini", &p));118check(profile_iterator_create(p, names, 0, &iter));119check(profile_iterator(&iter, &name, &value));120assert(name == NULL && value == NULL);121profile_iterator_free(&iter);122profile_abandon(p);123}124125/*126* Regression test for a 1.4-era bug where updating the underlying file data of127* a profile object lost track of the flag indicating that it was part of the128* global shared profiles list.129*/130static void131test_shared(void)132{133profile_t a, b;134struct utimbuf times;135136system("cp test2.ini test3.ini");137138/* Create an entry in the shared table. */139check(profile_init_path("test3.ini", &a));140141/*142* Force an update of the underlying data. With the bug present, the143* shared flag is erroneously cleared. The easiest way to force an update144* is to reopen the file (since we don't enforce the one-stat-per-second145* limit during open) after changing the timestamp.146*/147times.actime = time(NULL) + 2;148times.modtime = times.actime;149utime("test3.ini", ×);150check(profile_init_path("test3.ini", &b));151profile_release(b);152153/* Release the profile. With the bug present, a dangling reference is left154* behind in the shared table. */155profile_release(a);156157/* Open the profile again to dereference the dangling pointer if one was158* created. */159check(profile_init_path("test3.ini", &a));160profile_release(a);161}162163/* Regression test for #2950 (profile_clear_relation not reflected within164* handle where deletion is performed) */165static void166test_clear(void)167{168profile_t p;169const char *names[] = { "test section 1", "quux", NULL };170char **values, **dummy;171172check(profile_init_path("test2.ini", &p));173check(profile_get_values(p, names, &values));174check(profile_clear_relation(p, names));175check_fail(profile_get_values(p, names, &dummy), PROF_NO_RELATION);176check(profile_add_relation(p, names, values[0]));177profile_free_list(values);178check(profile_get_values(p, names, &values));179assert(values[0] != NULL && values[1] == NULL);180profile_free_list(values);181profile_abandon(p);182}183184static void185test_include(void)186{187profile_t p;188const char *names[] = { "test section 1", "bar", NULL };189char **values;190191/* Test expected error code when including nonexistent file. */192write_file("testinc.ini", 1, "include does-not-exist");193check_fail(profile_init_path("testinc.ini", &p), PROF_FAIL_INCLUDE_FILE);194195/* Test expected error code when including nonexistent directory. */196write_file("testinc.ini", 1, "includedir does-not-exist");197check_fail(profile_init_path("testinc.ini", &p), PROF_FAIL_INCLUDE_DIR);198199/* Test including a file. */200write_file("testinc.ini", 1, "include test2.ini");201check(profile_init_path("testinc.ini", &p));202check(profile_get_values(p, names, &values));203assert(strcmp(values[0], "foo") == 0 && values[1] == NULL);204profile_free_list(values);205profile_release(p);206207/*208* Test including a directory. Put four copies of test2.ini inside the209* directory, two with invalid names. Check that we get two values for one210* of the variables.211*/212system("rm -rf test_include_dir");213system("mkdir test_include_dir");214system("cp test2.ini test_include_dir/a");215system("cp test2.ini test_include_dir/a~");216system("cp test2.ini test_include_dir/b.conf");217system("cp test2.ini test_include_dir/b.conf.rpmsave");218write_file("testinc.ini", 1, "includedir test_include_dir");219check(profile_init_path("testinc.ini", &p));220check(profile_get_values(p, names, &values));221assert(strcmp(values[0], "foo") == 0);222assert(strcmp(values[1], "foo") == 0);223assert(values[2] == NULL);224profile_free_list(values);225profile_release(p);226227/* Directly list the directory in the profile path and try again. */228check(profile_init_path("test_include_dir", &p));229check(profile_get_values(p, names, &values));230assert(strcmp(values[0], "foo") == 0);231assert(strcmp(values[1], "foo") == 0);232assert(values[2] == NULL);233profile_free_list(values);234profile_release(p);235}236237/* Test syntactic independence of included profile files. */238static void239test_independence(void)240{241profile_t p;242const char *names1[] = { "sec1", "var", "a", NULL };243const char *names2[] = { "sec2", "b", NULL };244const char *names3[] = { "sec1", "var", "c", NULL };245char **values;246247write_file("testinc.ini", 6, "[sec1]", "var = {", "a = 1",248"include testinc2.ini", "c = 3", "}");249write_file("testinc2.ini", 2, "[sec2]", "b = 2");250251check(profile_init_path("testinc.ini", &p));252check(profile_get_values(p, names1, &values));253assert(strcmp(values[0], "1") == 0 && values[1] == NULL);254profile_free_list(values);255check(profile_get_values(p, names2, &values));256assert(strcmp(values[0], "2") == 0 && values[1] == NULL);257profile_free_list(values);258check(profile_get_values(p, names3, &values));259assert(strcmp(values[0], "3") == 0 && values[1] == NULL);260profile_free_list(values);261profile_release(p);262}263264/* Regression test for #7971 (deleted sections should not be iterable) */265static void266test_delete_section(void)267{268profile_t p;269const char *sect[] = { "test section 1", NULL };270const char *newrel[] = { "test section 1", "testkey", NULL };271const char *oldrel[] = { "test section 1", "child", NULL };272char **values;273274check(profile_init_path("test2.ini", &p));275276/* Remove and replace a section. */277check(profile_rename_section(p, sect, NULL));278check(profile_add_relation(p, sect, NULL));279check(profile_add_relation(p, newrel, "6"));280281/* Check that we can read the new relation but not the old one. */282check(profile_get_values(p, newrel, &values));283assert(strcmp(values[0], "6") == 0 && values[1] == NULL);284profile_free_list(values);285check_fail(profile_get_values(p, oldrel, &values), PROF_NO_RELATION);286profile_abandon(p);287}288289/* Regression test for #7971 (profile_clear_relation() error with deleted node290* at end of value set) */291static void292test_delete_clear_relation(void)293{294profile_t p;295const char *names[] = { "test section 1", "testkey", NULL };296297check(profile_init_path("test2.ini", &p));298check(profile_add_relation(p, names, "1"));299check(profile_add_relation(p, names, "2"));300check(profile_update_relation(p, names, "2", NULL));301check(profile_clear_relation(p, names));302profile_abandon(p);303}304305/* Test that order of relations is preserved if some relations are deleted. */306static void307test_delete_ordering(void)308{309profile_t p;310const char *names[] = { "test section 1", "testkey", NULL };311char **values;312313check(profile_init_path("test2.ini", &p));314check(profile_add_relation(p, names, "1"));315check(profile_add_relation(p, names, "2"));316check(profile_add_relation(p, names, "3"));317check(profile_update_relation(p, names, "2", NULL));318check(profile_add_relation(p, names, "4"));319check(profile_get_values(p, names, &values));320assert(strcmp(values[0], "1") == 0);321assert(strcmp(values[1], "3") == 0);322assert(strcmp(values[2], "4") == 0);323assert(values[3] == NULL);324profile_free_list(values);325profile_abandon(p);326}327328/* Regression test for #8431 (profile_flush_to_file erroneously changes flag329* state on source object) */330static void331test_flush_to_file(void)332{333profile_t p;334335/* Flush a profile object to a file without making any changes, so that the336* source object is still within g_shared_trees. */337check(profile_init_path("test2.ini", &p));338unlink("test3.ini");339check(profile_flush_to_file(p, "test3.ini"));340profile_release(p);341342/* Check for a dangling reference in g_shared_trees by creating another343* profile object. */344profile_init_path("test2.ini", &p);345profile_release(p);346}347348/* Regression test for #7863 (multiply-specified subsections should349* be merged) */350static void351test_merge_subsections(void)352{353profile_t p;354const char *n1[] = { "test section 2", "child_section2", "child", NULL };355const char *n2[] = { "test section 2", "child_section2", "chores", NULL };356char **values;357358check(profile_init_path("test2.ini", &p));359360check(profile_get_values(p, n1, &values));361assert(strcmp(values[0], "slick") == 0);362assert(strcmp(values[1], "harry") == 0);363assert(strcmp(values[2], "john\tb ") == 0);364assert(strcmp(values[3], "ron") == 0);365assert(values[4] == NULL);366profile_free_list(values);367368check(profile_get_values(p, n2, &values));369assert(strcmp(values[0], "cleaning") == 0 && values[1] == NULL);370profile_free_list(values);371372profile_release(p);373}374375/* Regression test for #9110 (null dereference when modifying an empty376* profile), and various other operations on an initially empty profile. */377static void378test_empty(void)379{380profile_t p, p2;381const char *n1[] = { "section", NULL };382const char *n2[] = { "section", "var", NULL };383char **values;384385check(profile_init(NULL, &p));386check(profile_add_relation(p, n1, NULL));387check(profile_add_relation(p, n2, "value"));388check(profile_flush(p)); /* should succeed but do nothing */389check(profile_get_values(p, n2, &values));390assert(strcmp(values[0], "value") == 0 && values[1] == NULL);391profile_free_list(values);392393check(profile_copy(p, &p2));394check(profile_get_values(p2, n2, &values));395assert(strcmp(values[0], "value") == 0 && values[1] == NULL);396profile_free_list(values);397profile_release(p2);398399profile_flush_to_file(p, "test3.ini");400profile_release(p);401402profile_init_path("test3.ini", &p);403check(profile_get_values(p, n2, &values));404assert(strcmp(values[0], "value") == 0 && values[1] == NULL);405profile_free_list(values);406profile_release(p);407}408409int410main(void)411{412test_iterate();413test_shared();414test_clear();415test_include();416test_independence();417test_delete_section();418test_delete_clear_relation();419test_delete_ordering();420test_flush_to_file();421test_merge_subsections();422test_empty();423}424425426