1 /* 2 * bytecode.c 3 * variable expansion bytecode evaluator 4 * 5 * SPDX-License-Identifier: pkgconf 6 * 7 * Copyright (c) 2026 pkgconf authors (see AUTHORS). 8 * 9 * Permission to use, copy, modify, and/or distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * This software is provided 'as is' and without any warranty, express or 14 * implied. In no event shall the authors be liable for any damages arising 15 * from the use of this software. 16 */ 17 18 #include <libpkgconf/stdinc.h> 19 #include <libpkgconf/libpkgconf.h> 20 21 /* 22 * !doc 23 * 24 * libpkgconf `bytecode` module 25 * ============================ 26 * 27 * The libpkgconf `bytecode` module contains the functions related to 28 * evaluating variable expansion bytecode. 29 */ 30 31 #define PKGCONF_EVAL_MAX_OUTPUT (PKGCONF_BUFSIZE - 1) 32 #define PKGCONF_EVAL_MAX_ITERATIONS (512) 33 34 static bool 35 pkgconf_bytecode_eval_append_slice(pkgconf_bytecode_eval_ctx_t *ctx, pkgconf_buffer_t *out, const char *p, size_t n) 36 { 37 size_t cur = pkgconf_buffer_len(out); 38 if (cur >= PKGCONF_EVAL_MAX_OUTPUT) 39 return false; 40 41 if (n > PKGCONF_EVAL_MAX_OUTPUT - cur) 42 { 43 pkgconf_warn(ctx->client, "warning: truncating very long variable to 64KB\n"); 44 45 n = PKGCONF_EVAL_MAX_OUTPUT - cur; 46 if (!pkgconf_buffer_append_slice(out, p, n)) 47 pkgconf_error(ctx->client, "pkgconf_bytecode_eval_append_slice: failed to append to slice"); 48 49 return false; 50 } 51 52 return pkgconf_buffer_append_slice(out, p, n); 53 } 54 55 static bool 56 pkgconf_bytecode_eval_append(pkgconf_bytecode_eval_ctx_t *ctx, pkgconf_buffer_t *out, const char *s) 57 { 58 if (s == NULL || *s == '\0') 59 return true; 60 61 return pkgconf_bytecode_eval_append_slice(ctx, out, s, strlen(s)); 62 } 63 64 static bool 65 pkgconf_bytecode_eval_internal(pkgconf_bytecode_eval_ctx_t *ctx, const pkgconf_bytecode_t *bc, pkgconf_buffer_t *out, bool *saw_sysroot); 66 67 static pkgconf_variable_t * 68 pkgconf_bytecode_eval_scan(const pkgconf_list_t *vars, const char *name, size_t nlen, unsigned int require_flags, unsigned int forbid_flags) 69 { 70 const pkgconf_node_t *node; 71 72 PKGCONF_FOREACH_LIST_ENTRY(vars->head, node) 73 { 74 pkgconf_variable_t *v = node->data; 75 76 if ((v->flags & require_flags) != require_flags) 77 continue; 78 79 if ((v->flags & forbid_flags) != 0) 80 continue; 81 82 if (pkgconf_str_eq_slice(v->key, name, nlen)) 83 return v; 84 } 85 86 return NULL; 87 } 88 89 pkgconf_variable_t * 90 pkgconf_bytecode_eval_lookup_var(pkgconf_bytecode_eval_ctx_t *ctx, const char *name, size_t nlen) 91 { 92 pkgconf_variable_t *v; 93 94 if ((v = pkgconf_bytecode_eval_scan(&ctx->client->global_vars, name, nlen, PKGCONF_VARIABLEF_OVERRIDE, 0)) != NULL) 95 return v; 96 97 if (ctx->vars != NULL && (v = pkgconf_bytecode_eval_scan(ctx->vars, name, nlen, 0, 0)) != NULL) 98 return v; 99 100 if ((v = pkgconf_bytecode_eval_scan(&ctx->client->global_vars, name, nlen, 0, PKGCONF_VARIABLEF_OVERRIDE)) != NULL) 101 return v; 102 103 return NULL; 104 } 105 106 static bool 107 pkgconf_bytecode_eval_var(pkgconf_bytecode_eval_ctx_t *ctx, const char *name, size_t nlen, pkgconf_buffer_t *out, bool *saw_sysroot) 108 { 109 pkgconf_variable_t *v; 110 111 v = pkgconf_bytecode_eval_lookup_var(ctx, name, nlen); 112 if (v == NULL) 113 return true; 114 115 if (v->expanding) 116 return false; 117 118 v->expanding = true; 119 120 bool inner_saw = false; 121 bool ok = pkgconf_bytecode_eval_internal(ctx, &v->bc, out, &inner_saw); 122 123 v->expanding = false; 124 125 if (!ok) 126 return false; 127 128 if (saw_sysroot != NULL) 129 *saw_sysroot |= inner_saw; 130 131 return true; 132 } 133 134 static bool 135 pkgconf_bytecode_eval_internal(pkgconf_bytecode_eval_ctx_t *ctx, const pkgconf_bytecode_t *bc, pkgconf_buffer_t *out, bool *saw_sysroot) 136 { 137 (void) ctx; 138 139 if (bc == NULL || out == NULL) 140 return false; 141 142 const uint8_t *p = bc->base; 143 const uint8_t *end = bc->base + bc->len; 144 145 if (++ctx->expansions > PKGCONF_EVAL_MAX_ITERATIONS) 146 { 147 pkgconf_warn(ctx->client, 148 "warning: bytecode program exceeds iteration limit (" SIZE_FMT_SPECIFIER ")\n", 149 ctx->expansions - 1); 150 return false; 151 } 152 153 while (p < end) 154 { 155 const pkgconf_bytecode_op_t *op = 156 (const pkgconf_bytecode_op_t *)p; 157 158 if ((const uint8_t *)op + sizeof(*op) > end) 159 return false; 160 161 if ((const uint8_t *)op + sizeof(*op) + op->size > end) 162 return false; 163 164 switch (op->tag) 165 { 166 case PKGCONF_BYTECODE_OP_TEXT: 167 /* this only fails due to truncation */ 168 if (!pkgconf_bytecode_eval_append_slice(ctx, out, op->data, op->size)) 169 return false; 170 break; 171 172 case PKGCONF_BYTECODE_OP_VAR: 173 if (!pkgconf_bytecode_eval_var(ctx, op->data, op->size, out, saw_sysroot)) 174 return false; 175 break; 176 177 case PKGCONF_BYTECODE_OP_SYSROOT: 178 if (saw_sysroot != NULL) 179 *saw_sysroot = true; 180 if (!pkgconf_bytecode_eval_append(ctx, out, pkgconf_buffer_str_or_empty(&ctx->sysroot))) 181 return false; 182 break; 183 184 default: 185 /* reserved/unimplemented */ 186 return false; 187 } 188 189 p = (const uint8_t *)pkgconf_bytecode_op_next(op); 190 } 191 192 return true; 193 } 194 195 static bool 196 pkgconf_bytecode_eval_ctx_init(pkgconf_bytecode_eval_ctx_t *ctx, const pkgconf_client_t *client, const pkgconf_list_t *vars) 197 { 198 memset(ctx, 0, sizeof(*ctx)); 199 200 ctx->client = client; 201 ctx->vars = vars; 202 203 const char *raw = pkgconf_client_get_sysroot_dir(client); 204 205 /* disabled sysroot cases */ 206 if (raw == NULL || *raw == '\0') 207 return true; 208 209 if (raw[0] == '.' && raw[1] == '\0') 210 return true; 211 212 if (raw[0] == '/' && raw[1] == '\0') 213 return true; 214 215 if (!pkgconf_buffer_append(&ctx->sysroot, raw)) 216 return false; 217 218 while (pkgconf_buffer_len(&ctx->sysroot) > 1 && ctx->sysroot.end[-1] == '/') 219 { 220 if (!pkgconf_buffer_trim_byte(&ctx->sysroot)) 221 return false; 222 } 223 224 /* if normalization yields "/", disable by making buffer empty */ 225 if (pkgconf_buffer_len(&ctx->sysroot) == 1 && ctx->sysroot.base[0] == '/') 226 { 227 if (!pkgconf_buffer_trim_byte(&ctx->sysroot)) 228 return false; 229 } 230 231 return true; 232 } 233 234 bool 235 pkgconf_bytecode_eval(const pkgconf_client_t *client, const pkgconf_list_t *vars, const pkgconf_bytecode_t *bc, pkgconf_buffer_t *out, bool *saw_sysroot) 236 { 237 bool ret; 238 239 if (client == NULL || bc == NULL || out == NULL) 240 return false; 241 242 pkgconf_bytecode_eval_ctx_t ctx; 243 if (!pkgconf_bytecode_eval_ctx_init(&ctx, client, vars)) 244 return false; 245 246 if (saw_sysroot != NULL) 247 *saw_sysroot = false; 248 249 ret = pkgconf_bytecode_eval_internal(&ctx, bc, out, saw_sysroot); 250 251 pkgconf_buffer_finalize(&ctx.sysroot); 252 253 return ret; 254 } 255 256 bool 257 pkgconf_bytecode_emit(pkgconf_buffer_t *buf, enum pkgconf_bytecode_op tag, const void *data, uint32_t size) 258 { 259 pkgconf_bytecode_op_t op = { 260 .tag = tag, 261 .size = size, 262 }; 263 264 if (!pkgconf_buffer_append_slice(buf, (const char *) &op, sizeof(op))) 265 return false; 266 267 if (size != 0) 268 { 269 if (!pkgconf_buffer_append_slice(buf, (const char *) data, (size_t) size)) 270 return false; 271 } 272 273 return true; 274 } 275 276 bool 277 pkgconf_bytecode_emit_text(pkgconf_buffer_t *buf, const char *p, size_t n) 278 { 279 if (p == NULL || n == 0) 280 return true; 281 282 return pkgconf_bytecode_emit(buf, PKGCONF_BYTECODE_OP_TEXT, p, (uint32_t) n); 283 } 284 285 bool 286 pkgconf_bytecode_emit_var(pkgconf_buffer_t *buf, const char *name, size_t nlen) 287 { 288 if (name == NULL || nlen == 0) 289 return true; 290 291 return pkgconf_bytecode_emit(buf, PKGCONF_BYTECODE_OP_VAR, name, (uint32_t) nlen); 292 } 293 294 bool 295 pkgconf_bytecode_emit_sysroot(pkgconf_buffer_t *buf) 296 { 297 return pkgconf_bytecode_emit(buf, PKGCONF_BYTECODE_OP_SYSROOT, NULL, 0); 298 } 299 300 void 301 pkgconf_bytecode_from_buffer(pkgconf_bytecode_t *bc, const pkgconf_buffer_t *buf) 302 { 303 bc->base = (const uint8_t *)buf->base; 304 bc->len = (size_t)(buf->end - buf->base); 305 } 306 307 bool 308 pkgconf_bytecode_compile(pkgconf_buffer_t *out, const char *value) 309 { 310 const char *p, *text_start; 311 312 if (out == NULL || value == NULL) 313 return false; 314 315 p = value; 316 text_start = value; 317 318 for (; *p != '\0'; p++) 319 { 320 const char *name, *q; 321 322 if (*p != '$') 323 continue; 324 325 /* $$ escapes to a literal $ */ 326 if (p[1] == '$') 327 { 328 if (p > text_start) 329 { 330 if (!pkgconf_bytecode_emit_text(out, text_start, (size_t)(p - text_start))) 331 return false; 332 } 333 334 if (!pkgconf_bytecode_emit_text(out, "$", 1)) 335 return false; 336 337 p++; 338 text_start = p + 1; 339 continue; 340 } 341 342 if (p[1] != '{') 343 continue; 344 345 if (p > text_start) 346 { 347 if (!pkgconf_bytecode_emit_text(out, text_start, (size_t)(p - text_start))) 348 return false; 349 } 350 351 name = p + 2; 352 q = name; 353 354 for (; *q != '\0' && *q != '}'; q++) 355 ; 356 357 /* make sure a variable expansion ends with } */ 358 if (*q != '}') 359 { 360 text_start = p; 361 continue; 362 } 363 364 /* if this is not a valid variable, emit it as text */ 365 size_t nlen = (size_t)(q - name); 366 if (nlen == 0 || nlen >= PKGCONF_ITEM_SIZE) 367 { 368 if (!pkgconf_bytecode_emit_text(out, p, (size_t)((q + 1) - p))) 369 return false; 370 371 p = q; 372 text_start = p + 1; 373 continue; 374 } 375 376 /* we need to special-case ${pc_sysrootdir} and emit OP_SYSROOT instead... */ 377 if (nlen == strlen("pc_sysrootdir") && !memcmp(name, "pc_sysrootdir", nlen)) 378 { 379 if (!pkgconf_bytecode_emit_sysroot(out)) 380 return false; 381 } 382 else 383 { 384 if (!pkgconf_bytecode_emit_var(out, name, nlen)) 385 return false; 386 } 387 388 p = q; 389 text_start = p + 1; 390 } 391 392 if (p > text_start) 393 { 394 if (!pkgconf_bytecode_emit_text(out, text_start, (size_t)(p - text_start))) 395 return false; 396 } 397 398 return true; 399 } 400 401 bool 402 pkgconf_bytecode_eval_str_to_buf(const pkgconf_client_t *client, const pkgconf_list_t *vars, const char *input, bool *saw_sysroot, pkgconf_buffer_t *out) 403 { 404 pkgconf_buffer_t bcbuf = PKGCONF_BUFFER_INITIALIZER; 405 pkgconf_bytecode_t bc; 406 bool ret = false; 407 408 if (!pkgconf_bytecode_compile(&bcbuf, input)) 409 { 410 pkgconf_buffer_finalize(&bcbuf); 411 return false; 412 } 413 414 pkgconf_bytecode_from_buffer(&bc, &bcbuf); 415 416 ret = pkgconf_bytecode_eval(client, vars, &bc, out, saw_sysroot); 417 418 pkgconf_buffer_finalize(&bcbuf); 419 420 return ret; 421 } 422 423 char * 424 pkgconf_bytecode_eval_str(const pkgconf_client_t *client, const pkgconf_list_t *vars, const char *input, bool *saw_sysroot) 425 { 426 pkgconf_buffer_t out = PKGCONF_BUFFER_INITIALIZER; 427 428 if (!pkgconf_bytecode_eval_str_to_buf(client, vars, input, saw_sysroot, &out)) 429 { 430 if (pkgconf_buffer_len(&out) > 0) 431 return pkgconf_buffer_freeze(&out); 432 433 pkgconf_buffer_finalize(&out); 434 return NULL; 435 } 436 437 if (pkgconf_buffer_len(&out) == 0) 438 { 439 pkgconf_buffer_finalize(&out); 440 return strdup(""); 441 } 442 443 return pkgconf_buffer_freeze(&out); 444 } 445 446 bool 447 pkgconf_bytecode_references_var(const pkgconf_buffer_t *buf, const char *key) 448 { 449 const uint8_t *p, *end; 450 size_t klen; 451 452 if (buf == NULL || key == NULL) 453 return false; 454 455 klen = strlen(key); 456 p = (uint8_t *) buf->base; 457 end = (uint8_t *) buf->end; 458 459 while (p < end) 460 { 461 const pkgconf_bytecode_op_t *op = (const pkgconf_bytecode_op_t *)p; 462 463 if (p + sizeof(*op) > end) 464 return false; 465 466 if (p + sizeof(*op) + op->size > end) 467 return false; 468 469 if (op->tag == PKGCONF_BYTECODE_OP_VAR) 470 { 471 if (op->size == (uint32_t) klen && memcmp(op->data, key, klen) == 0) 472 return true; 473 } 474 475 p += sizeof(*op) + op->size; 476 } 477 478 return false; 479 } 480 481 static bool 482 pkgconf_bytecode_op_is_selfref(const pkgconf_bytecode_op_t *op, const char *key) 483 { 484 const size_t klen = strlen(key); 485 486 if (op->tag != PKGCONF_BYTECODE_OP_VAR) 487 return false; 488 489 if (op->size != (uint32_t) klen) 490 return false; 491 492 return memcmp(op->data, key, klen) == 0; 493 } 494 495 static bool 496 pkgconf_bytecode_append_stream(pkgconf_buffer_t *dst, const pkgconf_buffer_t *bcbuf) 497 { 498 if (dst == NULL || bcbuf == NULL || pkgconf_buffer_str(bcbuf) == NULL) 499 return true; 500 501 return pkgconf_buffer_append_slice(dst, pkgconf_buffer_str(bcbuf), pkgconf_buffer_len(bcbuf)); 502 } 503 504 bool 505 pkgconf_bytecode_rewrite_selfrefs(pkgconf_buffer_t *out, const pkgconf_buffer_t *rhs, const char *key, const pkgconf_buffer_t *prev) 506 { 507 const uint8_t *p = (uint8_t *) rhs->base; 508 const uint8_t *end = (uint8_t *) rhs->end; 509 510 while (p < end) 511 { 512 const pkgconf_bytecode_op_t *op = (const pkgconf_bytecode_op_t *)p; 513 514 if (p + sizeof(*op) > end) 515 return false; 516 517 if (p + sizeof(*op) + op->size > end) 518 return false; 519 520 if (pkgconf_bytecode_op_is_selfref(op, key)) 521 { 522 if (!pkgconf_bytecode_append_stream(out, prev)) 523 return false; 524 } 525 else 526 { 527 if (!pkgconf_buffer_append_slice(out, (const char *) op, sizeof(*op) + op->size)) 528 return false; 529 } 530 531 p += sizeof(*op) + op->size; 532 } 533 534 return true; 535 } 536