Home | History | Annotate | Download | only in audit
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright 2002-2003 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include "abi_audit.h"
     30 
     31 /* Variables */
     32 int			Debug;
     33 static const char 	*Numstr = "01234567689.";
     34 
     35 /* Internal functions */
     36 static int		compare_ver_inc(char *, char *);
     37 static void		check_ver_inc(liblist_t *);
     38 static void		check_lib_ver(liblist_t *, char *);
     39 static void		check_sym_ver(symbol_t *);
     40 
     41 /*
     42  * assign_versions() will assign vt_lib_ver and vt_sym_ver for a given release.
     43  * lib_ver should be the highest version of the library and sym_ver should be
     44  * the lowest version this symbol was exported.
     45  * The intf_check output will be read in with the highest version of the
     46  * library first.  So, for a new release, the lib_ver will need to be set,
     47  * whereas, on subsequent calls to this function, only the sym_ver needs to be
     48  * reset.
     49  */
     50 
     51 void
     52 assign_versions(liblist_t *orig_lib, liblist_t *new_lib, int rel_num)
     53 {
     54 	char *oldlibver;
     55 	char *newlibver;
     56 	char *newsymver;
     57 
     58 	oldlibver = get_lib_ver(orig_lib, rel_num);
     59 
     60 	/* Start of a new release */
     61 	if (oldlibver == NULL) {
     62 		newlibver = get_lib_ver(new_lib, rel_num);
     63 		assign_lib_ver(orig_lib, newlibver, rel_num);
     64 	}
     65 
     66 	newsymver = get_sym_ver(new_lib, rel_num);
     67 	assign_sym_ver(orig_lib, newsymver, rel_num);
     68 }
     69 
     70 /*
     71  * Returns the first release which is exported.  Returns FAIL if the symbol was
     72  * never exported
     73  */
     74 
     75 int
     76 find_exported_release(liblist_t *lib, int first_exported)
     77 {
     78 	int		release = 0;
     79 	int		count = 0;
     80 	bvlist_t	*bitmask;
     81 
     82 	bitmask =  get_rel_bitmask(0);
     83 
     84 	while (count < Total_relcnt) {
     85 		if ((bv_and(bitmask, lib->lt_release) == TRUE) &&
     86 		    (bv_and(bitmask, lib->lt_cat->ct_unexported) != TRUE)) {
     87 			if (first_exported)
     88 				return (count);
     89 			release = count;
     90 		}
     91 		bitmask = bv_bitmask_rshift(bitmask);
     92 		count ++;
     93 	}
     94 
     95 	/*
     96 	 * symbol was never exported or it was scoped local during its lifetime.
     97 	 */
     98 	if ((!get_lib_ver(lib, release)) &&
     99 	    (bv_all_zero(lib->lt_cat->ct_public) == TRUE) &&
    100 	    (bv_all_zero(lib->lt_cat->ct_private) == TRUE)) {
    101 		free_bv_list(bitmask);
    102 		return (FAIL);
    103 	}
    104 	free_bv_list(bitmask);
    105 	return (release);
    106 }
    107 
    108 /*
    109  * Version Checker recursively checks for versioning errors in each node of
    110  * the tree.
    111  */
    112 
    113 void
    114 version_checker(tree_t *rootptr)
    115 {
    116 	if (rootptr) {
    117 		version_checker(rootptr->tt_left);
    118 		check_sym_ver(rootptr->tt_sym);
    119 		version_checker(rootptr->tt_right);
    120 	}
    121 }
    122 
    123 /*
    124  * check_sym_ver() will traverse through the liblist_t and ensure
    125  * versioning policies are maintained in each library of a given symbol.
    126  */
    127 
    128 static void
    129 check_sym_ver(symbol_t *symbol)
    130 {
    131 	liblist_t	*lib = symbol->st_lib;
    132 
    133 	while (lib != NULL) {
    134 		check_lib_ver(lib, symbol->st_sym_name);
    135 		lib = lib->lt_next;
    136 	}
    137 }
    138 
    139 /*
    140  * check_lib_ver() prints error if:
    141  * 	- base version is not maintained
    142  * 	- base_vers is not < lib_vers
    143  * 	- version incrementing between new releases of a lib does not
    144  * 	follow library versioning policies of incrementing by only ".1"
    145  */
    146 
    147 static void
    148 check_lib_ver(liblist_t *lib, char *sym_name)
    149 {
    150 	int	i = 0;
    151 	int	first_vers, prev_ver_num, var;
    152 	char	*libver;
    153 	char	*next_libver;
    154 	char	*prev_sym_ver, *curr_sym_ver;
    155 
    156 	if (!lib->lt_check_me) {
    157 		return;
    158 	}
    159 	if ((first_vers = find_exported_release(lib, 1)) == FAIL) {
    160 		if (Debug)
    161 			(void) fprintf(stderr,
    162 				"%s: check_lib_ver: %s was never exported",
    163 				program, sym_name);
    164 	}
    165 	if (!iflag) {
    166 		if (get_sym_ver(lib, Total_relcnt - 2) != NULL) {
    167 			/* symbol existed in last Solaris Release */
    168 			first_vers = Total_relcnt - 2;
    169 		} else {
    170 			/* symbol is unexported in last Solaris Release */
    171 			first_vers = Total_relcnt - 1;
    172 		}
    173 	}
    174 	curr_sym_ver = get_sym_ver(lib, first_vers);
    175 	prev_ver_num = first_vers;
    176 
    177 	for (i = first_vers; i < Total_relcnt; i ++) {
    178 		libver = get_lib_ver(lib, i);
    179 
    180 		/*
    181 		 * flag WARNING if SUNWobsolete->SUNWprivate_m.o
    182 		 * flag ERROR if SUNWobsolete->SUNW_m.n.o or any new version
    183 		 * ASSERTION check to make sure it does not happen
    184 		 */
    185 		if (libver && (strstr(libver, "SUNWobsolete") != NULL) &&
    186 		    (i < Total_relcnt - 1)) {
    187 			next_libver = get_lib_ver(lib, i + 1);
    188 			if (next_libver) {
    189 				if (strstr(next_libver, "SUNWprivate") != NULL)
    190 					(void) fprintf(Msgout, "WARNING: ");
    191 				else
    192 					(void) fprintf(Msgout, "ERROR: ");
    193 				(void) fprintf(Msgout,
    194 				    "%s: %s->%s: ", lib->lt_lib_name, libver,
    195 				    next_libver);
    196 				(void) fprintf(Msgout,
    197 				    "new interface introduced to the "
    198 				    "obsolete library\n");
    199 			}
    200 		}
    201 
    202 		/*
    203 		 * only do base and new version checking for public symbols.
    204 		 * current_sym_ver can be NULL for releases in which the
    205 		 * symbol is unexported.
    206 		 */
    207 		if (!get_sym_ver(lib, i)) {
    208 			continue;
    209 		}
    210 		if (!curr_sym_ver) {
    211 			continue;
    212 		}
    213 		prev_sym_ver = curr_sym_ver;
    214 		curr_sym_ver = get_sym_ver(lib, i);
    215 
    216 		if ((strstr(curr_sym_ver, "SUNW_") == NULL) ||
    217 		    (strstr(prev_sym_ver, "SUNW_") == NULL)) {
    218 			continue;
    219 		}
    220 
    221 		/*
    222 		 * ensure the base version is maintained through all
    223 		 * releases of a library
    224 		 */
    225 		if (strcmp(curr_sym_ver, prev_sym_ver) != 0) {
    226 			(void) fprintf(Msgout,
    227 			    "ERROR: %s: %s: "
    228 			    "base version not maintained, was %s in %s, "
    229 			    "becomes %s in ",
    230 			    lib->lt_lib_name, sym_name, prev_sym_ver,
    231 			    get_rel_name(prev_ver_num), curr_sym_ver);
    232 			if (i != (Total_relcnt - 1)) {
    233 				(void) fprintf(Msgout, "%s\n",
    234 				    get_rel_name(i));
    235 			} else {
    236 				(void) fprintf(Msgout, "current release\n");
    237 			}
    238 		}
    239 
    240 		/*
    241 		 * ensure new symbols are versioned with the
    242 		 * highest lib_version.  Sym's base version must equal
    243 		 * it's highest lib_version.
    244 		 */
    245 		if (libver &&
    246 		    ((lib->lt_scenario == SCENARIO_01) ||
    247 		    (lib->lt_scenario == SCENARIO_05))) {
    248 
    249 			/*
    250 			 * new symbols could have been introduced in update
    251 			 * releases and they have to be versioned in micro
    252 			 * number if only a subset of the new symbols are
    253 			 * backported from the market release to a update
    254 			 * or patch release.  The new version name needs
    255 			 * to be validated.
    256 			 */
    257 			if (strcmp(libver, curr_sym_ver) != 0) {
    258 				if (!iflag &&
    259 				    (first_vers != Total_relcnt - 1)) {
    260 					continue;
    261 				}
    262 				if ((var =
    263 				    count_num_char('.', curr_sym_ver) == 1) ||
    264 				    ((var == 2) &&
    265 				    (compare_ver_inc(curr_sym_ver, libver)
    266 				    == FAIL))) {
    267 					(void) fprintf(Msgout,
    268 					    "ERROR: %s: %s:"
    269 					    " invalid new version, %s "
    270 					    "should be %s in ",
    271 					    lib->lt_lib_name, sym_name,
    272 					    curr_sym_ver, libver);
    273 					if (i != (Total_relcnt - 1)) {
    274 						(void) fprintf(Msgout, "%s\n",
    275 						    get_rel_name(i));
    276 					} else {
    277 						(void) fprintf(Msgout,
    278 						    "current release\n");
    279 					}
    280 				}
    281 				break;
    282 			}
    283 		}
    284 		prev_ver_num = i;
    285 	}
    286 	/*
    287 	 * ensure that the version is incremented by at most ".1"
    288 	 * between previous and new releases of a library
    289 	 */
    290 	check_ver_inc(lib);
    291 }
    292 
    293 /*
    294  * It ensures the integrity of version incrementing is maintained between
    295  * multiple releases of a library. If not, an error message is printed
    296  */
    297 
    298 static void
    299 check_ver_inc(liblist_t *lib)
    300 {
    301 	char	*prev_ver;
    302 	int	prev_ver_num;
    303 	char	*current_ver;
    304 	int	i;
    305 	int	first_rel;
    306 
    307 	if ((first_rel = find_exported_release(lib, 1)) == FAIL) {
    308 		if (Debug)
    309 			(void) fprintf(stderr,
    310 				"%s: check_ver_inc: never exported", program);
    311 	}
    312 	/* current_ver can only be NULL when a symbol is always scoped_local */
    313 	if ((current_ver = get_lib_ver(lib, first_rel)) == NULL)
    314 		return;
    315 
    316 	/*
    317 	 * Only check versioning for public symbols, but skip checking of
    318 	 * SYSVABI and SISCD standards versions
    319 	 */
    320 	if ((strstr(current_ver, "SUNW_") == NULL) ||
    321 	    (first_rel+1 >= Total_relcnt))
    322 		return;
    323 
    324 	/* prev_ver refers to first release this symbol was exported */
    325 	prev_ver_num = first_rel;
    326 
    327 	/*
    328 	 * compare prev_ver to current_ver by always comparing the versions
    329 	 * pairwise.  Reset prev_ver to the last version exported.
    330 	 */
    331 	for (i = first_rel + 1; i < Total_relcnt; i ++) {
    332 
    333 		if (get_lib_ver(lib, i) != NULL) {
    334 			prev_ver = current_ver;
    335 			current_ver = get_lib_ver(lib, i);
    336 
    337 			/*
    338 			 * check to make sure that increments between releases
    339 			 * of a lib are no greater than .1
    340 			 */
    341 			if ((strstr(current_ver, "SUNW_") != NULL) &&
    342 			    (compare_ver_inc(prev_ver, current_ver) == FAIL)) {
    343 				if (!iflag && (i < Total_relcnt - 1)) {
    344 					continue;
    345 				}
    346 				(void) fprintf(Msgout,
    347 				    "ERROR: %s: was %s in %s, becomes %s in ",
    348 				    lib->lt_lib_name, prev_ver,
    349 				    get_rel_name(prev_ver_num), current_ver);
    350 				if (i != (Total_relcnt - 1)) {
    351 					(void) fprintf(Msgout, "%s:",
    352 					    get_rel_name(i));
    353 				} else {
    354 					(void) fprintf(Msgout,
    355 					    "current release:");
    356 				}
    357 				(void) fprintf(Msgout,
    358 				    " inconsistent increment of version\n");
    359 			}
    360 			prev_ver_num = i;
    361 		}
    362 	}
    363 }
    364 
    365 /*
    366  * compare_ver_inc() checks the major, minor and micro numbers
    367  * of two public version strings to ensure that the new version hasn't been
    368  * incremented by more than ".1" per release. Versioning prior to SUNW_1.1
    369  * is ignored.  Strings matching "SUNW_m.n.o" where "o" is optional, will
    370  * be checked for this version incrementing scheme.
    371  */
    372 
    373 static int
    374 compare_ver_inc(char *old_version, char *new_version)
    375 {
    376 	char	*oversion;
    377 	char 	*nversion;
    378 	int 	omajor, nmajor, ominor, nminor;
    379 	int	omicro = 0;
    380 	int 	nmicro = 0;
    381 
    382 	/* scoped local symbols contain no version numbering */
    383 	if ((strstr(old_version, "_LOCAL_") != NULL) &&
    384 	    (strstr(new_version, "_LOCAL_") != NULL)) {
    385 		return (SUCCEED);
    386 	}
    387 
    388 	if (((oversion = strpbrk(old_version, Numstr)) == NULL) ||
    389 	    ((nversion = strpbrk(new_version, Numstr)) == NULL)) {
    390 		return (FAIL);
    391 	}
    392 
    393 	/* extracting major, minor and micro number from old version */
    394 	omajor = atoi(oversion);
    395 	if ((oversion = strchr(oversion, '.')) == NULL) {
    396 		return (FAIL);
    397 	}
    398 	oversion ++;
    399 	ominor = atoi(oversion);
    400 	if ((oversion = strrchr(oversion, '.')) != NULL) {
    401 		oversion ++;
    402 		omicro = atoi(oversion);
    403 	}
    404 
    405 	/* extracting major, minor and micro number from new version */
    406 	nmajor = atoi(nversion);
    407 	if ((nversion = strchr(nversion, '.')) == NULL) {
    408 		return (FAIL);
    409 	}
    410 	nversion ++;
    411 	nminor = atoi(nversion);
    412 	if ((nversion = strrchr(nversion, '.')) != NULL) {
    413 		nversion ++;
    414 		nmicro = atoi(nversion);
    415 	}
    416 
    417 	/* new symbol should be versioned with major >= 1 & minor >= 1 */
    418 	if (omajor == 0 && ominor == 0 && (nmajor < 1 || nminor < 1))
    419 		return (FAIL);
    420 
    421 	/* ignore all versioning prior to 1.1 */
    422 	if (omajor == 0 && nmajor == 1 && nminor == 1)
    423 		return (SUCCEED);
    424 
    425 	/*
    426 	 * omajor < nmajor; incompatible changes within a library
    427 	 * i.e., SUNW_1.n.o vs. SUNW_2.n.o
    428 	 * omajor > nmajor; impossible to have within a library
    429 	 * i.e., SUNW_2.n.o vs. SUNW_1.n.o
    430 	 */
    431 	if (omajor < nmajor || omajor > nmajor) {
    432 		return (FAIL);
    433 
    434 	/* omajor == nmajor */
    435 	} else if (omajor == nmajor) {
    436 		/*
    437 		 * m stays, n is incremented with optional o
    438 		 */
    439 		if (ominor < nminor) {
    440 			/* i.e., SUNW_1.2	vs. SUNW_1.4 */
    441 			if (ominor + 1 != nminor)
    442 				return (FAIL);
    443 			/* i.e., SUNW_1.2	vs. SUNW_1.3.1 */
    444 			/* i.e., SUNW_1.2.1	vs. SUNW_1.3.1 */
    445 			else if (nmicro != 0)
    446 				return (FAIL);
    447 			else
    448 				return (SUCCEED);
    449 		/*
    450 		 * m & n stay the same but o is incremented
    451 		 */
    452 		} else if (ominor == nminor) {
    453 				/* old m.n.o equals to new m.n.o */
    454 				/* i.e., SUNW_1.20.3 vs. SUNW_1.20.3 */
    455 				if (omicro == nmicro)
    456 					return (SUCCEED);
    457 				/* i.e., SUNW_1.20	vs. SUNW_1.20.2 */
    458 				/* i.e., SUNW_1.20.1	vs. SUNW_1.20.3 */
    459 				if (omicro + 1 != nmicro)
    460 					return (FAIL);
    461 				else
    462 					/* i.e., SUNW_1.20 vs. SUNW_1.20.1 */
    463 					/* i.e., SUNW_1.8.2 vs. SUNW_1.8.3 */
    464 					return (SUCCEED);
    465 		/*
    466 		 * m stays & old minor > new minor version
    467 		 * i.e., SUNW_1.21	vs. SUNW_1.20
    468 		 */
    469 		} else if (ominor > nminor)
    470 			return (FAIL);
    471 		else
    472 			return (SUCCEED);
    473 	} else {
    474 		/* It's impossible to reach here but just in case */
    475 		(void) fprintf(Msgout, "ERROR: verschk.c:compare_ver_inc():");
    476 		(void) fprintf(Msgout, "%d.%d.%d <-> %d.%d.%d\n",
    477 		    omajor, ominor, omicro, nmajor, nminor, nmicro);
    478 	}
    479 	return (FAIL);
    480 }
    481