1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2019-2022 Red Hat, Inc. Daniel Bristot de Oliveira <bristot@kernel.org> 4 * 5 * This is the online Runtime Verification (RV) interface. 6 * 7 * RV is a lightweight (yet rigorous) method that complements classical 8 * exhaustive verification techniques (such as model checking and 9 * theorem proving) with a more practical approach to complex systems. 10 * 11 * RV works by analyzing the trace of the system's actual execution, 12 * comparing it against a formal specification of the system behavior. 13 * RV can give precise information on the runtime behavior of the 14 * monitored system while enabling the reaction for unexpected 15 * events, avoiding, for example, the propagation of a failure on 16 * safety-critical systems. 17 * 18 * The development of this interface roots in the development of the 19 * paper: 20 * 21 * De Oliveira, Daniel Bristot; Cucinotta, Tommaso; De Oliveira, Romulo 22 * Silva. Efficient formal verification for the Linux kernel. In: 23 * International Conference on Software Engineering and Formal Methods. 24 * Springer, Cham, 2019. p. 315-332. 25 * 26 * And: 27 * 28 * De Oliveira, Daniel Bristot, et al. Automata-based formal analysis 29 * and verification of the real-time Linux kernel. PhD Thesis, 2020. 30 * 31 * == Runtime monitor interface == 32 * 33 * A monitor is the central part of the runtime verification of a system. 34 * 35 * The monitor stands in between the formal specification of the desired 36 * (or undesired) behavior, and the trace of the actual system. 37 * 38 * In Linux terms, the runtime verification monitors are encapsulated 39 * inside the "RV monitor" abstraction. A RV monitor includes a reference 40 * model of the system, a set of instances of the monitor (per-cpu monitor, 41 * per-task monitor, and so on), and the helper functions that glue the 42 * monitor to the system via trace. Generally, a monitor includes some form 43 * of trace output as a reaction for event parsing and exceptions, 44 * as depicted below: 45 * 46 * Linux +----- RV Monitor ----------------------------------+ Formal 47 * Realm | | Realm 48 * +-------------------+ +----------------+ +-----------------+ 49 * | Linux kernel | | Monitor | | Reference | 50 * | Tracing | -> | Instance(s) | <- | Model | 51 * | (instrumentation) | | (verification) | | (specification) | 52 * +-------------------+ +----------------+ +-----------------+ 53 * | | | 54 * | V | 55 * | +----------+ | 56 * | | Reaction | | 57 * | +--+--+--+-+ | 58 * | | | | | 59 * | | | +-> trace output ? | 60 * +------------------------|--|----------------------+ 61 * | +----> panic ? 62 * +-------> <user-specified> 63 * 64 * This file implements the interface for loading RV monitors, and 65 * to control the verification session. 66 * 67 * == Registering monitors == 68 * 69 * The struct rv_monitor defines a set of callback functions to control 70 * a verification session. For instance, when a given monitor is enabled, 71 * the "enable" callback function is called to hook the instrumentation 72 * functions to the kernel trace events. The "disable" function is called 73 * when disabling the verification session. 74 * 75 * A RV monitor is registered via: 76 * int rv_register_monitor(struct rv_monitor *monitor); 77 * And unregistered via: 78 * int rv_unregister_monitor(struct rv_monitor *monitor); 79 * 80 * == User interface == 81 * 82 * The user interface resembles kernel tracing interface. It presents 83 * these files: 84 * 85 * "available_monitors" 86 * - List the available monitors, one per line. 87 * 88 * For example: 89 * # cat available_monitors 90 * wip 91 * wwnr 92 * 93 * "enabled_monitors" 94 * - Lists the enabled monitors, one per line; 95 * - Writing to it enables a given monitor; 96 * - Writing a monitor name with a '!' prefix disables it; 97 * - Truncating the file disables all enabled monitors. 98 * 99 * For example: 100 * # cat enabled_monitors 101 * # echo wip > enabled_monitors 102 * # echo wwnr >> enabled_monitors 103 * # cat enabled_monitors 104 * wip 105 * wwnr 106 * # echo '!wip' >> enabled_monitors 107 * # cat enabled_monitors 108 * wwnr 109 * # echo > enabled_monitors 110 * # cat enabled_monitors 111 * # 112 * 113 * Note that more than one monitor can be enabled concurrently. 114 * 115 * "monitoring_on" 116 * - It is an on/off general switcher for monitoring. Note 117 * that it does not disable enabled monitors or detach events, 118 * but stops the per-entity monitors from monitoring the events 119 * received from the instrumentation. It resembles the "tracing_on" 120 * switcher. 121 * 122 * "monitors/" 123 * Each monitor will have its own directory inside "monitors/". There 124 * the monitor specific files will be presented. 125 * The "monitors/" directory resembles the "events" directory on 126 * tracefs. 127 * 128 * For example: 129 * # cd monitors/wip/ 130 * # ls 131 * desc enable 132 * # cat desc 133 * auto-generated wakeup in preemptive monitor. 134 * # cat enable 135 * 0 136 * 137 * For further information, see: 138 * Documentation/trace/rv/runtime-verification.rst 139 */ 140 141 #include <linux/kernel.h> 142 #include <linux/module.h> 143 #include <linux/init.h> 144 #include <linux/slab.h> 145 146 #ifdef CONFIG_RV_MON_EVENTS 147 #define CREATE_TRACE_POINTS 148 #include <rv_trace.h> 149 #endif 150 151 #include "rv.h" 152 153 DEFINE_MUTEX(rv_interface_lock); 154 155 static struct rv_interface rv_root; 156 157 struct dentry *get_monitors_root(void) 158 { 159 return rv_root.monitors_dir; 160 } 161 162 /* 163 * Interface for the monitor register. 164 */ 165 LIST_HEAD(rv_monitors_list); 166 167 static bool task_monitor_slots[CONFIG_RV_PER_TASK_MONITORS]; 168 169 int rv_get_task_monitor_slot(void) 170 { 171 int i; 172 173 lockdep_assert_held(&rv_interface_lock); 174 175 for (i = 0; i < CONFIG_RV_PER_TASK_MONITORS; i++) { 176 if (!task_monitor_slots[i]) { 177 task_monitor_slots[i] = true; 178 return i; 179 } 180 } 181 182 return -EBUSY; 183 } 184 185 void rv_put_task_monitor_slot(int slot) 186 { 187 lockdep_assert_held(&rv_interface_lock); 188 189 if (slot < 0 || slot >= CONFIG_RV_PER_TASK_MONITORS) { 190 WARN_ONCE(1, "RV releasing an invalid slot!: %d\n", slot); 191 return; 192 } 193 194 if (WARN_ONCE(!task_monitor_slots[slot], 195 "RV releasing unused task monitor slot: %d\n", slot)) 196 return; 197 198 task_monitor_slots[slot] = false; 199 } 200 201 /* 202 * Monitors with a parent are nested, 203 * Monitors without a parent could be standalone or containers. 204 */ 205 bool rv_is_nested_monitor(struct rv_monitor *mon) 206 { 207 return mon->parent != NULL; 208 } 209 210 /* 211 * We set our list to have nested monitors listed after their parent 212 * if a monitor has a child element its a container. 213 * Containers can be also identified based on their function pointers: 214 * as they are not real monitors they do not need function definitions 215 * for enable()/disable(). Use this condition to find empty containers. 216 * Keep both conditions in case we have some non-compliant containers. 217 */ 218 bool rv_is_container_monitor(struct rv_monitor *mon) 219 { 220 struct rv_monitor *next; 221 222 if (list_is_last(&mon->list, &rv_monitors_list)) 223 return false; 224 225 next = list_next_entry(mon, list); 226 227 return next->parent == mon || !mon->enable; 228 } 229 230 /* 231 * This section collects the monitor/ files and folders. 232 */ 233 static ssize_t monitor_enable_read_data(struct file *filp, char __user *user_buf, size_t count, 234 loff_t *ppos) 235 { 236 struct rv_monitor *mon = filp->private_data; 237 const char *buff; 238 239 buff = mon->enabled ? "1\n" : "0\n"; 240 241 return simple_read_from_buffer(user_buf, count, ppos, buff, strlen(buff)+1); 242 } 243 244 /* 245 * __rv_disable_monitor - disabled an enabled monitor 246 */ 247 static int __rv_disable_monitor(struct rv_monitor *mon, bool sync) 248 { 249 lockdep_assert_held(&rv_interface_lock); 250 251 if (mon->enabled) { 252 mon->enabled = 0; 253 if (mon->disable) 254 mon->disable(); 255 256 /* 257 * Wait for the execution of all events to finish. 258 * Otherwise, the data used by the monitor could 259 * be inconsistent. i.e., if the monitor is re-enabled. 260 */ 261 if (sync) 262 tracepoint_synchronize_unregister(); 263 return 1; 264 } 265 return 0; 266 } 267 268 static void rv_disable_single(struct rv_monitor *mon) 269 { 270 __rv_disable_monitor(mon, true); 271 } 272 273 static int rv_enable_single(struct rv_monitor *mon) 274 { 275 int retval; 276 277 lockdep_assert_held(&rv_interface_lock); 278 279 if (mon->enabled) 280 return 0; 281 282 retval = mon->enable(); 283 284 if (!retval) 285 mon->enabled = 1; 286 287 return retval; 288 } 289 290 static void rv_disable_container(struct rv_monitor *mon) 291 { 292 struct rv_monitor *p = mon; 293 int enabled = 0; 294 295 list_for_each_entry_continue(p, &rv_monitors_list, list) { 296 if (p->parent != mon) 297 break; 298 enabled += __rv_disable_monitor(p, false); 299 } 300 if (enabled) 301 tracepoint_synchronize_unregister(); 302 mon->enabled = 0; 303 } 304 305 static int rv_enable_container(struct rv_monitor *mon) 306 { 307 struct rv_monitor *p = mon; 308 int retval = 0; 309 310 list_for_each_entry_continue(p, &rv_monitors_list, list) { 311 if (retval || p->parent != mon) 312 break; 313 retval = rv_enable_single(p); 314 } 315 if (retval) 316 rv_disable_container(mon); 317 else 318 mon->enabled = 1; 319 return retval; 320 } 321 322 /** 323 * rv_disable_monitor - disable a given runtime monitor 324 * @mon: Pointer to the monitor definition structure. 325 * 326 * Returns 0 on success. 327 */ 328 int rv_disable_monitor(struct rv_monitor *mon) 329 { 330 if (rv_is_container_monitor(mon)) 331 rv_disable_container(mon); 332 else 333 rv_disable_single(mon); 334 335 return 0; 336 } 337 338 /** 339 * rv_enable_monitor - enable a given runtime monitor 340 * @mon: Pointer to the monitor definition structure. 341 * 342 * Returns 0 on success, error otherwise. 343 */ 344 int rv_enable_monitor(struct rv_monitor *mon) 345 { 346 int retval; 347 348 if (rv_is_container_monitor(mon)) 349 retval = rv_enable_container(mon); 350 else 351 retval = rv_enable_single(mon); 352 353 return retval; 354 } 355 356 /* 357 * interface for enabling/disabling a monitor. 358 */ 359 static ssize_t monitor_enable_write_data(struct file *filp, const char __user *user_buf, 360 size_t count, loff_t *ppos) 361 { 362 struct rv_monitor *mon = filp->private_data; 363 int retval; 364 bool val; 365 366 retval = kstrtobool_from_user(user_buf, count, &val); 367 if (retval) 368 return retval; 369 370 guard(mutex)(&rv_interface_lock); 371 372 if (val) 373 retval = rv_enable_monitor(mon); 374 else 375 retval = rv_disable_monitor(mon); 376 377 return retval ? : count; 378 } 379 380 static const struct file_operations interface_enable_fops = { 381 .open = simple_open, 382 .write = monitor_enable_write_data, 383 .read = monitor_enable_read_data, 384 }; 385 386 /* 387 * Interface to read monitors description. 388 */ 389 static ssize_t monitor_desc_read_data(struct file *filp, char __user *user_buf, size_t count, 390 loff_t *ppos) 391 { 392 struct rv_monitor *mon = filp->private_data; 393 char buff[256]; 394 395 memset(buff, 0, sizeof(buff)); 396 397 snprintf(buff, sizeof(buff), "%s\n", mon->description); 398 399 return simple_read_from_buffer(user_buf, count, ppos, buff, strlen(buff) + 1); 400 } 401 402 static const struct file_operations interface_desc_fops = { 403 .open = simple_open, 404 .read = monitor_desc_read_data, 405 }; 406 407 /* 408 * During the registration of a monitor, this function creates 409 * the monitor dir, where the specific options of the monitor 410 * are exposed. 411 */ 412 static int create_monitor_dir(struct rv_monitor *mon, struct rv_monitor *parent) 413 { 414 struct dentry *root = parent ? parent->root_d : get_monitors_root(); 415 struct dentry *dir __free(rv_remove) = rv_create_dir(mon->name, root); 416 struct dentry *tmp; 417 int retval; 418 419 if (!dir) 420 return -ENOMEM; 421 422 tmp = rv_create_file("enable", RV_MODE_WRITE, dir, mon, &interface_enable_fops); 423 if (!tmp) 424 return -ENOMEM; 425 426 tmp = rv_create_file("desc", RV_MODE_READ, dir, mon, &interface_desc_fops); 427 if (!tmp) 428 return -ENOMEM; 429 430 retval = reactor_populate_monitor(mon, dir); 431 if (retval) 432 return retval; 433 434 mon->root_d = no_free_ptr(dir); 435 return 0; 436 } 437 438 /* 439 * Available/Enable monitor shared seq functions. 440 */ 441 static int monitors_show(struct seq_file *m, void *p) 442 { 443 struct rv_monitor *mon = container_of(p, struct rv_monitor, list); 444 445 if (mon->parent) 446 seq_printf(m, "%s:%s\n", mon->parent->name, mon->name); 447 else 448 seq_printf(m, "%s\n", mon->name); 449 return 0; 450 } 451 452 /* 453 * Used by the seq file operations at the end of a read 454 * operation. 455 */ 456 static void monitors_stop(struct seq_file *m, void *p) 457 { 458 mutex_unlock(&rv_interface_lock); 459 } 460 461 /* 462 * Available monitor seq functions. 463 */ 464 static void *available_monitors_start(struct seq_file *m, loff_t *pos) 465 { 466 mutex_lock(&rv_interface_lock); 467 return seq_list_start(&rv_monitors_list, *pos); 468 } 469 470 static void *available_monitors_next(struct seq_file *m, void *p, loff_t *pos) 471 { 472 return seq_list_next(p, &rv_monitors_list, pos); 473 } 474 475 /* 476 * Enable monitor seq functions. 477 */ 478 static void *enabled_monitors_next(struct seq_file *m, void *p, loff_t *pos) 479 { 480 struct rv_monitor *mon = container_of(p, struct rv_monitor, list); 481 482 (*pos)++; 483 484 list_for_each_entry_continue(mon, &rv_monitors_list, list) { 485 if (mon->enabled) 486 return &mon->list; 487 } 488 489 return NULL; 490 } 491 492 static void *enabled_monitors_start(struct seq_file *m, loff_t *pos) 493 { 494 struct list_head *head; 495 loff_t l; 496 497 mutex_lock(&rv_interface_lock); 498 499 if (list_empty(&rv_monitors_list)) 500 return NULL; 501 502 head = &rv_monitors_list; 503 504 for (l = 0; l <= *pos; ) { 505 head = enabled_monitors_next(m, head, &l); 506 if (!head) 507 break; 508 } 509 510 return head; 511 } 512 513 /* 514 * available/enabled monitors seq definition. 515 */ 516 static const struct seq_operations available_monitors_seq_ops = { 517 .start = available_monitors_start, 518 .next = available_monitors_next, 519 .stop = monitors_stop, 520 .show = monitors_show 521 }; 522 523 static const struct seq_operations enabled_monitors_seq_ops = { 524 .start = enabled_monitors_start, 525 .next = enabled_monitors_next, 526 .stop = monitors_stop, 527 .show = monitors_show 528 }; 529 530 /* 531 * available_monitors interface. 532 */ 533 static int available_monitors_open(struct inode *inode, struct file *file) 534 { 535 return seq_open(file, &available_monitors_seq_ops); 536 }; 537 538 static const struct file_operations available_monitors_ops = { 539 .open = available_monitors_open, 540 .read = seq_read, 541 .llseek = seq_lseek, 542 .release = seq_release 543 }; 544 545 /* 546 * enabled_monitors interface. 547 */ 548 static void disable_all_monitors(void) 549 { 550 struct rv_monitor *mon; 551 int enabled = 0; 552 553 guard(mutex)(&rv_interface_lock); 554 555 list_for_each_entry(mon, &rv_monitors_list, list) 556 enabled += __rv_disable_monitor(mon, false); 557 558 if (enabled) { 559 /* 560 * Wait for the execution of all events to finish. 561 * Otherwise, the data used by the monitor could 562 * be inconsistent. i.e., if the monitor is re-enabled. 563 */ 564 tracepoint_synchronize_unregister(); 565 } 566 } 567 568 static int enabled_monitors_open(struct inode *inode, struct file *file) 569 { 570 if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) 571 disable_all_monitors(); 572 573 return seq_open(file, &enabled_monitors_seq_ops); 574 }; 575 576 static ssize_t enabled_monitors_write(struct file *filp, const char __user *user_buf, 577 size_t count, loff_t *ppos) 578 { 579 char buff[MAX_RV_MONITOR_NAME_SIZE + 2]; 580 struct rv_monitor *mon; 581 int retval = -EINVAL; 582 bool enable = true; 583 char *ptr, *tmp; 584 int len; 585 586 if (count < 1 || count > MAX_RV_MONITOR_NAME_SIZE + 1) 587 return -EINVAL; 588 589 memset(buff, 0, sizeof(buff)); 590 591 retval = simple_write_to_buffer(buff, sizeof(buff) - 1, ppos, user_buf, count); 592 if (retval < 0) 593 return -EFAULT; 594 595 ptr = strim(buff); 596 597 if (ptr[0] == '!') { 598 enable = false; 599 ptr++; 600 } 601 602 len = strlen(ptr); 603 if (!len) 604 return count; 605 606 guard(mutex)(&rv_interface_lock); 607 608 retval = -EINVAL; 609 610 /* we support 1 nesting level, trim the parent */ 611 tmp = strstr(ptr, ":"); 612 if (tmp) 613 ptr = tmp+1; 614 615 list_for_each_entry(mon, &rv_monitors_list, list) { 616 if (strcmp(ptr, mon->name) != 0) 617 continue; 618 619 /* 620 * Monitor found! 621 */ 622 if (enable) 623 retval = rv_enable_monitor(mon); 624 else 625 retval = rv_disable_monitor(mon); 626 627 if (retval) 628 return retval; 629 return count; 630 } 631 632 return retval; 633 } 634 635 static const struct file_operations enabled_monitors_ops = { 636 .open = enabled_monitors_open, 637 .read = seq_read, 638 .write = enabled_monitors_write, 639 .llseek = seq_lseek, 640 .release = seq_release, 641 }; 642 643 /* 644 * Monitoring on global switcher! 645 */ 646 static bool __read_mostly monitoring_on; 647 648 /** 649 * rv_monitoring_on - checks if monitoring is on 650 * 651 * Returns 1 if on, 0 otherwise. 652 */ 653 bool rv_monitoring_on(void) 654 { 655 return READ_ONCE(monitoring_on); 656 } 657 658 /* 659 * monitoring_on general switcher. 660 */ 661 static ssize_t monitoring_on_read_data(struct file *filp, char __user *user_buf, 662 size_t count, loff_t *ppos) 663 { 664 const char *buff; 665 666 buff = rv_monitoring_on() ? "1\n" : "0\n"; 667 668 return simple_read_from_buffer(user_buf, count, ppos, buff, strlen(buff) + 1); 669 } 670 671 static void turn_monitoring_off(void) 672 { 673 WRITE_ONCE(monitoring_on, false); 674 } 675 676 static void reset_all_monitors(void) 677 { 678 struct rv_monitor *mon; 679 680 list_for_each_entry(mon, &rv_monitors_list, list) { 681 if (mon->enabled && mon->reset) 682 mon->reset(); 683 } 684 } 685 686 static void turn_monitoring_on(void) 687 { 688 WRITE_ONCE(monitoring_on, true); 689 } 690 691 static void turn_monitoring_on_with_reset(void) 692 { 693 lockdep_assert_held(&rv_interface_lock); 694 695 if (rv_monitoring_on()) 696 return; 697 698 /* 699 * Monitors might be out of sync with the system if events were not 700 * processed because of !rv_monitoring_on(). 701 * 702 * Reset all monitors, forcing a re-sync. 703 */ 704 reset_all_monitors(); 705 turn_monitoring_on(); 706 } 707 708 static ssize_t monitoring_on_write_data(struct file *filp, const char __user *user_buf, 709 size_t count, loff_t *ppos) 710 { 711 int retval; 712 bool val; 713 714 retval = kstrtobool_from_user(user_buf, count, &val); 715 if (retval) 716 return retval; 717 718 guard(mutex)(&rv_interface_lock); 719 720 if (val) 721 turn_monitoring_on_with_reset(); 722 else 723 turn_monitoring_off(); 724 725 /* 726 * Wait for the execution of all events to finish 727 * before returning to user-space. 728 */ 729 tracepoint_synchronize_unregister(); 730 731 return count; 732 } 733 734 static const struct file_operations monitoring_on_fops = { 735 .open = simple_open, 736 .write = monitoring_on_write_data, 737 .read = monitoring_on_read_data, 738 }; 739 740 static void destroy_monitor_dir(struct rv_monitor *mon) 741 { 742 rv_remove(mon->root_d); 743 } 744 745 /** 746 * rv_register_monitor - register a rv monitor. 747 * @monitor: The rv_monitor to be registered. 748 * @parent: The parent of the monitor to be registered, NULL if not nested. 749 * 750 * Returns 0 if successful, error otherwise. 751 */ 752 int rv_register_monitor(struct rv_monitor *monitor, struct rv_monitor *parent) 753 { 754 struct rv_monitor *r; 755 int retval = 0; 756 757 if (strlen(monitor->name) >= MAX_RV_MONITOR_NAME_SIZE) { 758 pr_info("Monitor %s has a name longer than %d\n", monitor->name, 759 MAX_RV_MONITOR_NAME_SIZE); 760 return -EINVAL; 761 } 762 763 guard(mutex)(&rv_interface_lock); 764 765 list_for_each_entry(r, &rv_monitors_list, list) { 766 if (strcmp(monitor->name, r->name) == 0) { 767 pr_info("Monitor %s is already registered\n", monitor->name); 768 return -EEXIST; 769 } 770 } 771 772 if (parent && rv_is_nested_monitor(parent)) { 773 pr_info("Parent monitor %s is already nested, cannot nest further\n", 774 parent->name); 775 return -EINVAL; 776 } 777 778 monitor->parent = parent; 779 780 retval = create_monitor_dir(monitor, parent); 781 if (retval) 782 return retval; 783 784 /* keep children close to the parent for easier visualisation */ 785 if (parent) 786 list_add(&monitor->list, &parent->list); 787 else 788 list_add_tail(&monitor->list, &rv_monitors_list); 789 790 return 0; 791 } 792 793 /** 794 * rv_unregister_monitor - unregister a rv monitor. 795 * @monitor: The rv_monitor to be unregistered. 796 * 797 * Returns 0 if successful, error otherwise. 798 */ 799 int rv_unregister_monitor(struct rv_monitor *monitor) 800 { 801 guard(mutex)(&rv_interface_lock); 802 803 rv_disable_monitor(monitor); 804 list_del(&monitor->list); 805 destroy_monitor_dir(monitor); 806 807 return 0; 808 } 809 810 int __init rv_init_interface(void) 811 { 812 struct dentry *tmp; 813 int retval; 814 struct dentry *root_dir __free(rv_remove) = rv_create_dir("rv", NULL); 815 816 if (!root_dir) 817 return 1; 818 819 rv_root.monitors_dir = rv_create_dir("monitors", root_dir); 820 if (!rv_root.monitors_dir) 821 return 1; 822 823 tmp = rv_create_file("available_monitors", RV_MODE_READ, root_dir, NULL, 824 &available_monitors_ops); 825 if (!tmp) 826 return 1; 827 828 tmp = rv_create_file("enabled_monitors", RV_MODE_WRITE, root_dir, NULL, 829 &enabled_monitors_ops); 830 if (!tmp) 831 return 1; 832 833 tmp = rv_create_file("monitoring_on", RV_MODE_WRITE, root_dir, NULL, 834 &monitoring_on_fops); 835 if (!tmp) 836 return 1; 837 retval = init_rv_reactors(root_dir); 838 if (retval) 839 return 1; 840 841 turn_monitoring_on(); 842 843 rv_root.root_dir = no_free_ptr(root_dir); 844 845 return 0; 846 } 847