1 2 #include "libcflat.h" 3 4 #define smp_id() 0 5 6 #define true 1 7 #define false 0 8 9 static _Bool verbose = false; 10 11 typedef unsigned long pt_element_t; 12 13 #define PAGE_SIZE ((pt_element_t)4096) 14 #define PAGE_MASK (~(PAGE_SIZE-1)) 15 16 #define PT_BASE_ADDR_MASK ((pt_element_t)((((pt_element_t)1 << 40) - 1) & PAGE_MASK)) 17 #define PT_PSE_BASE_ADDR_MASK (PT_BASE_ADDR_MASK & ~(1ull << 21)) 18 19 #define PT_PRESENT_MASK ((pt_element_t)1 << 0) 20 #define PT_WRITABLE_MASK ((pt_element_t)1 << 1) 21 #define PT_USER_MASK ((pt_element_t)1 << 2) 22 #define PT_ACCESSED_MASK ((pt_element_t)1 << 5) 23 #define PT_DIRTY_MASK ((pt_element_t)1 << 6) 24 #define PT_PSE_MASK ((pt_element_t)1 << 7) 25 #define PT_NX_MASK ((pt_element_t)1 << 63) 26 27 #define CR0_WP_MASK (1UL << 16) 28 29 #define PFERR_PRESENT_MASK (1U << 0) 30 #define PFERR_WRITE_MASK (1U << 1) 31 #define PFERR_USER_MASK (1U << 2) 32 #define PFERR_RESERVED_MASK (1U << 3) 33 #define PFERR_FETCH_MASK (1U << 4) 34 35 #define MSR_EFER 0xc0000080 36 #define EFER_NX_MASK (1ull << 11) 37 38 /* 39 * page table access check tests 40 */ 41 42 enum { 43 AC_PTE_PRESENT, 44 AC_PTE_WRITABLE, 45 AC_PTE_USER, 46 AC_PTE_ACCESSED, 47 AC_PTE_DIRTY, 48 AC_PTE_NX, 49 AC_PTE_BIT51, 50 51 AC_PDE_PRESENT, 52 AC_PDE_WRITABLE, 53 AC_PDE_USER, 54 AC_PDE_ACCESSED, 55 AC_PDE_DIRTY, 56 AC_PDE_PSE, 57 AC_PDE_NX, 58 AC_PDE_BIT51, 59 60 AC_ACCESS_USER, 61 AC_ACCESS_WRITE, 62 AC_ACCESS_FETCH, 63 AC_ACCESS_TWICE, 64 // AC_ACCESS_PTE, 65 66 AC_CPU_EFER_NX, 67 AC_CPU_CR0_WP, 68 69 NR_AC_FLAGS 70 }; 71 72 const char *ac_names[] = { 73 [AC_PTE_PRESENT] = "pte.p", 74 [AC_PTE_ACCESSED] = "pte.a", 75 [AC_PTE_WRITABLE] = "pte.rw", 76 [AC_PTE_USER] = "pte.user", 77 [AC_PTE_DIRTY] = "pte.d", 78 [AC_PTE_NX] = "pte.nx", 79 [AC_PTE_BIT51] = "pte.51", 80 [AC_PDE_PRESENT] = "pde.p", 81 [AC_PDE_ACCESSED] = "pde.a", 82 [AC_PDE_WRITABLE] = "pde.rw", 83 [AC_PDE_USER] = "pde.user", 84 [AC_PDE_DIRTY] = "pde.d", 85 [AC_PDE_PSE] = "pde.pse", 86 [AC_PDE_NX] = "pde.nx", 87 [AC_PDE_BIT51] = "pde.51", 88 [AC_ACCESS_WRITE] = "write", 89 [AC_ACCESS_USER] = "user", 90 [AC_ACCESS_FETCH] = "fetch", 91 [AC_ACCESS_TWICE] = "twice", 92 [AC_CPU_EFER_NX] = "efer.nx", 93 [AC_CPU_CR0_WP] = "cr0.wp", 94 }; 95 96 static inline void *va(pt_element_t phys) 97 { 98 return (void *)phys; 99 } 100 101 static unsigned long read_cr0() 102 { 103 unsigned long cr0; 104 105 asm volatile ("mov %%cr0, %0" : "=r"(cr0)); 106 107 return cr0; 108 } 109 110 static void write_cr0(unsigned long cr0) 111 { 112 asm volatile ("mov %0, %%cr0" : : "r"(cr0)); 113 } 114 115 typedef struct { 116 unsigned short offset0; 117 unsigned short selector; 118 unsigned short ist : 3; 119 unsigned short : 5; 120 unsigned short type : 4; 121 unsigned short : 1; 122 unsigned short dpl : 2; 123 unsigned short p : 1; 124 unsigned short offset1; 125 unsigned offset2; 126 unsigned reserved; 127 } idt_entry_t; 128 129 typedef struct { 130 pt_element_t pt_pool; 131 unsigned pt_pool_size; 132 unsigned pt_pool_current; 133 } ac_pool_t; 134 135 typedef struct { 136 unsigned flags[NR_AC_FLAGS]; 137 void *virt; 138 pt_element_t phys; 139 pt_element_t *ptep; 140 pt_element_t expected_pte; 141 pt_element_t *pdep; 142 pt_element_t expected_pde; 143 pt_element_t ignore_pde; 144 int expected_fault; 145 unsigned expected_error; 146 idt_entry_t idt[256]; 147 } ac_test_t; 148 149 typedef struct { 150 unsigned short limit; 151 unsigned long linear_addr; 152 } __attribute__((packed)) descriptor_table_t; 153 154 155 static void ac_test_show(ac_test_t *at); 156 157 void lidt(idt_entry_t *idt, int nentries) 158 { 159 descriptor_table_t dt; 160 161 dt.limit = nentries * sizeof(*idt) - 1; 162 dt.linear_addr = (unsigned long)idt; 163 asm volatile ("lidt %0" : : "m"(dt)); 164 } 165 166 unsigned short read_cs() 167 { 168 unsigned short r; 169 170 asm volatile ("mov %%cs, %0" : "=r"(r)); 171 return r; 172 } 173 174 unsigned long long rdmsr(unsigned index) 175 { 176 unsigned a, d; 177 178 asm volatile("rdmsr" : "=a"(a), "=d"(d) : "c"(index)); 179 return ((unsigned long long)d << 32) | a; 180 } 181 182 void wrmsr(unsigned index, unsigned long long val) 183 { 184 unsigned a = val, d = val >> 32; 185 186 asm volatile("wrmsr" : : "a"(a), "d"(d), "c"(index)); 187 } 188 189 void set_idt_entry(idt_entry_t *e, void *addr, int dpl) 190 { 191 memset(e, 0, sizeof *e); 192 e->offset0 = (unsigned long)addr; 193 e->selector = read_cs(); 194 e->ist = 0; 195 e->type = 14; 196 e->dpl = dpl; 197 e->p = 1; 198 e->offset1 = (unsigned long)addr >> 16; 199 e->offset2 = (unsigned long)addr >> 32; 200 } 201 202 void set_cr0_wp(int wp) 203 { 204 unsigned long cr0 = read_cr0(); 205 206 cr0 &= ~CR0_WP_MASK; 207 if (wp) 208 cr0 |= CR0_WP_MASK; 209 write_cr0(cr0); 210 } 211 212 void set_efer_nx(int nx) 213 { 214 unsigned long long efer; 215 216 efer = rdmsr(MSR_EFER); 217 efer &= ~EFER_NX_MASK; 218 if (nx) 219 efer |= EFER_NX_MASK; 220 wrmsr(MSR_EFER, efer); 221 } 222 223 static void ac_env_int(ac_pool_t *pool) 224 { 225 static idt_entry_t idt[256]; 226 227 memset(idt, 0, sizeof(idt)); 228 lidt(idt, 256); 229 extern char page_fault, kernel_entry; 230 set_idt_entry(&idt[14], &page_fault, 0); 231 set_idt_entry(&idt[0x20], &kernel_entry, 3); 232 233 pool->pt_pool = 33 * 1024 * 1024; 234 pool->pt_pool_size = 120 * 1024 * 1024 - pool->pt_pool; 235 pool->pt_pool_current = 0; 236 } 237 238 void ac_test_init(ac_test_t *at, void *virt) 239 { 240 wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_NX_MASK); 241 set_cr0_wp(1); 242 for (int i = 0; i < NR_AC_FLAGS; ++i) 243 at->flags[i] = 0; 244 at->virt = virt; 245 at->phys = 32 * 1024 * 1024; 246 } 247 248 int ac_test_bump_one(ac_test_t *at) 249 { 250 for (int i = 0; i < NR_AC_FLAGS; ++i) 251 if (!at->flags[i]) { 252 at->flags[i] = 1; 253 return 1; 254 } else 255 at->flags[i] = 0; 256 return 0; 257 } 258 259 _Bool ac_test_legal(ac_test_t *at) 260 { 261 if (at->flags[AC_ACCESS_FETCH] && at->flags[AC_ACCESS_WRITE]) 262 return false; 263 return true; 264 } 265 266 int ac_test_bump(ac_test_t *at) 267 { 268 int ret; 269 270 ret = ac_test_bump_one(at); 271 while (ret && !ac_test_legal(at)) 272 ret = ac_test_bump_one(at); 273 return ret; 274 } 275 276 unsigned long read_cr3() 277 { 278 unsigned long cr3; 279 280 asm volatile ("mov %%cr3, %0" : "=r"(cr3)); 281 return cr3; 282 } 283 284 void invlpg(void *addr) 285 { 286 asm volatile ("invlpg (%0)" : : "r"(addr)); 287 } 288 289 pt_element_t ac_test_alloc_pt(ac_pool_t *pool) 290 { 291 pt_element_t ret = pool->pt_pool + pool->pt_pool_current; 292 pool->pt_pool_current += PAGE_SIZE; 293 return ret; 294 } 295 296 _Bool ac_test_enough_room(ac_pool_t *pool) 297 { 298 return pool->pt_pool_current + 4 * PAGE_SIZE <= pool->pt_pool_size; 299 } 300 301 void ac_test_reset_pt_pool(ac_pool_t *pool) 302 { 303 pool->pt_pool_current = 0; 304 } 305 306 void ac_set_expected_status(ac_test_t *at) 307 { 308 int pde_valid, pte_valid; 309 310 invlpg(at->virt); 311 312 if (at->ptep) 313 at->expected_pte = *at->ptep; 314 at->expected_pde = *at->pdep; 315 at->ignore_pde = 0; 316 at->expected_fault = 0; 317 at->expected_error = PFERR_PRESENT_MASK; 318 319 pde_valid = at->flags[AC_PDE_PRESENT] 320 && !at->flags[AC_PDE_BIT51] 321 && !(at->flags[AC_PDE_NX] && !at->flags[AC_CPU_EFER_NX]); 322 pte_valid = pde_valid 323 && at->flags[AC_PTE_PRESENT] 324 && !at->flags[AC_PTE_BIT51] 325 && !(at->flags[AC_PTE_NX] && !at->flags[AC_CPU_EFER_NX]); 326 if (at->flags[AC_ACCESS_TWICE]) { 327 if (pde_valid) { 328 at->expected_pde |= PT_ACCESSED_MASK; 329 if (pte_valid) 330 at->expected_pte |= PT_ACCESSED_MASK; 331 } 332 } 333 334 if (at->flags[AC_ACCESS_USER]) 335 at->expected_error |= PFERR_USER_MASK; 336 337 if (at->flags[AC_ACCESS_WRITE]) 338 at->expected_error |= PFERR_WRITE_MASK; 339 340 if (at->flags[AC_ACCESS_FETCH]) 341 at->expected_error |= PFERR_FETCH_MASK; 342 343 if (!at->flags[AC_PDE_PRESENT]) { 344 at->expected_fault = 1; 345 at->expected_error &= ~PFERR_PRESENT_MASK; 346 } else if (!pde_valid) { 347 at->expected_fault = 1; 348 at->expected_error |= PFERR_RESERVED_MASK; 349 } 350 351 if (at->flags[AC_ACCESS_USER] && !at->flags[AC_PDE_USER]) 352 at->expected_fault = 1; 353 354 if (at->flags[AC_ACCESS_WRITE] 355 && !at->flags[AC_PDE_WRITABLE] 356 && (at->flags[AC_CPU_CR0_WP] || at->flags[AC_ACCESS_USER])) 357 at->expected_fault = 1; 358 359 if (at->flags[AC_ACCESS_FETCH] && at->flags[AC_PDE_NX]) 360 at->expected_fault = 1; 361 362 if (!at->flags[AC_PDE_ACCESSED]) 363 at->ignore_pde = PT_ACCESSED_MASK; 364 365 if (!pde_valid) 366 goto fault; 367 368 if (!at->expected_fault) 369 at->expected_pde |= PT_ACCESSED_MASK; 370 371 if (at->flags[AC_PDE_PSE]) { 372 if (at->flags[AC_ACCESS_WRITE] && !at->expected_fault) 373 at->expected_pde |= PT_DIRTY_MASK; 374 goto no_pte; 375 } 376 377 if (!at->flags[AC_PTE_PRESENT]) { 378 at->expected_fault = 1; 379 at->expected_error &= ~PFERR_PRESENT_MASK; 380 } else if (!pte_valid) { 381 at->expected_fault = 1; 382 at->expected_error |= PFERR_RESERVED_MASK; 383 } 384 385 if (at->flags[AC_ACCESS_USER] && !at->flags[AC_PTE_USER]) 386 at->expected_fault = 1; 387 388 if (at->flags[AC_ACCESS_WRITE] 389 && !at->flags[AC_PTE_WRITABLE] 390 && (at->flags[AC_CPU_CR0_WP] || at->flags[AC_ACCESS_USER])) 391 at->expected_fault = 1; 392 393 if (at->flags[AC_ACCESS_FETCH] && at->flags[AC_PTE_NX]) 394 at->expected_fault = 1; 395 396 if (at->expected_fault) 397 goto fault; 398 399 at->expected_pte |= PT_ACCESSED_MASK; 400 if (at->flags[AC_ACCESS_WRITE]) 401 at->expected_pte |= PT_DIRTY_MASK; 402 403 no_pte: 404 fault: 405 if (!at->expected_fault) 406 at->ignore_pde = 0; 407 if (!at->flags[AC_CPU_EFER_NX]) 408 at->expected_error &= ~PFERR_FETCH_MASK; 409 } 410 411 void ac_test_setup_pte(ac_test_t *at, ac_pool_t *pool) 412 { 413 unsigned long root = read_cr3(); 414 415 if (!ac_test_enough_room(pool)) 416 ac_test_reset_pt_pool(pool); 417 418 at->ptep = 0; 419 for (int i = 4; i >= 1 && (i >= 2 || !at->flags[AC_PDE_PSE]); --i) { 420 pt_element_t *vroot = va(root & PT_BASE_ADDR_MASK); 421 unsigned index = ((unsigned long)at->virt >> (12 + (i-1) * 9)) & 511; 422 pt_element_t pte = 0; 423 switch (i) { 424 case 4: 425 case 3: 426 pte = vroot[index]; 427 pte = ac_test_alloc_pt(pool) | PT_PRESENT_MASK; 428 pte |= PT_WRITABLE_MASK | PT_USER_MASK; 429 break; 430 case 2: 431 if (!at->flags[AC_PDE_PSE]) 432 pte = ac_test_alloc_pt(pool); 433 else { 434 pte = at->phys & PT_PSE_BASE_ADDR_MASK; 435 pte |= PT_PSE_MASK; 436 } 437 if (at->flags[AC_PDE_PRESENT]) 438 pte |= PT_PRESENT_MASK; 439 if (at->flags[AC_PDE_WRITABLE]) 440 pte |= PT_WRITABLE_MASK; 441 if (at->flags[AC_PDE_USER]) 442 pte |= PT_USER_MASK; 443 if (at->flags[AC_PDE_ACCESSED]) 444 pte |= PT_ACCESSED_MASK; 445 if (at->flags[AC_PDE_DIRTY]) 446 pte |= PT_DIRTY_MASK; 447 if (at->flags[AC_PDE_NX]) 448 pte |= PT_NX_MASK; 449 if (at->flags[AC_PDE_BIT51]) 450 pte |= 1ull << 51; 451 at->pdep = &vroot[index]; 452 break; 453 case 1: 454 pte = at->phys & PT_BASE_ADDR_MASK; 455 if (at->flags[AC_PTE_PRESENT]) 456 pte |= PT_PRESENT_MASK; 457 if (at->flags[AC_PTE_WRITABLE]) 458 pte |= PT_WRITABLE_MASK; 459 if (at->flags[AC_PTE_USER]) 460 pte |= PT_USER_MASK; 461 if (at->flags[AC_PTE_ACCESSED]) 462 pte |= PT_ACCESSED_MASK; 463 if (at->flags[AC_PTE_DIRTY]) 464 pte |= PT_DIRTY_MASK; 465 if (at->flags[AC_PTE_NX]) 466 pte |= PT_NX_MASK; 467 if (at->flags[AC_PTE_BIT51]) 468 pte |= 1ull << 51; 469 at->ptep = &vroot[index]; 470 break; 471 } 472 vroot[index] = pte; 473 root = vroot[index]; 474 } 475 ac_set_expected_status(at); 476 } 477 478 static void ac_test_check(ac_test_t *at, _Bool *success_ret, _Bool cond, 479 const char *fmt, ...) 480 { 481 va_list ap; 482 char buf[500]; 483 484 if (!*success_ret) { 485 return; 486 } 487 488 if (!cond) { 489 return; 490 } 491 492 *success_ret = false; 493 494 if (!verbose) { 495 ac_test_show(at); 496 } 497 498 va_start(ap, fmt); 499 vsnprintf(buf, sizeof(buf), fmt, ap); 500 va_end(ap); 501 printf("FAIL: %s\n", buf); 502 } 503 504 static int pt_match(pt_element_t pte1, pt_element_t pte2, pt_element_t ignore) 505 { 506 pte1 &= ~ignore; 507 pte2 &= ~ignore; 508 return pte1 == pte2; 509 } 510 511 int ac_test_do_access(ac_test_t *at) 512 { 513 static unsigned unique = 42; 514 int fault = 0; 515 unsigned e; 516 static unsigned char user_stack[4096]; 517 unsigned long rsp; 518 _Bool success = true; 519 520 ++unique; 521 522 *((unsigned char *)at->phys) = 0xc3; /* ret */ 523 524 unsigned r = unique; 525 set_cr0_wp(at->flags[AC_CPU_CR0_WP]); 526 set_efer_nx(at->flags[AC_CPU_EFER_NX]); 527 528 if (at->flags[AC_ACCESS_TWICE]) { 529 asm volatile ( 530 "mov $fixed2, %%rsi \n\t" 531 "mov (%[addr]), %[reg] \n\t" 532 "fixed2:" 533 : [reg]"=r"(r), [fault]"=a"(fault), "=b"(e) 534 : [addr]"r"(at->virt) 535 : "rsi" 536 ); 537 fault = 0; 538 } 539 540 asm volatile ("mov $fixed1, %%rsi \n\t" 541 "mov %%rsp, %%rdx \n\t" 542 "cmp $0, %[user] \n\t" 543 "jz do_access \n\t" 544 "push %%rax; mov %[user_ds], %%ax; mov %%ax, %%ds; pop %%rax \n\t" 545 "pushq %[user_ds] \n\t" 546 "pushq %[user_stack_top] \n\t" 547 "pushfq \n\t" 548 "pushq %[user_cs] \n\t" 549 "pushq $do_access \n\t" 550 "iretq \n" 551 "do_access: \n\t" 552 "cmp $0, %[fetch] \n\t" 553 "jnz 2f \n\t" 554 "cmp $0, %[write] \n\t" 555 "jnz 1f \n\t" 556 "mov (%[addr]), %[reg] \n\t" 557 "jmp done \n\t" 558 "1: mov %[reg], (%[addr]) \n\t" 559 "jmp done \n\t" 560 "2: call *%[addr] \n\t" 561 "done: \n" 562 "fixed1: \n" 563 "int %[kernel_entry_vector] \n\t" 564 "back_to_kernel:" 565 : [reg]"+r"(r), "+a"(fault), "=b"(e), "=&d"(rsp) 566 : [addr]"r"(at->virt), 567 [write]"r"(at->flags[AC_ACCESS_WRITE]), 568 [user]"r"(at->flags[AC_ACCESS_USER]), 569 [fetch]"r"(at->flags[AC_ACCESS_FETCH]), 570 [user_ds]"i"(32+3), 571 [user_cs]"i"(24+3), 572 [user_stack_top]"r"(user_stack + sizeof user_stack), 573 [kernel_entry_vector]"i"(0x20) 574 : "rsi"); 575 576 asm volatile (".section .text.pf \n\t" 577 "page_fault: \n\t" 578 "pop %rbx \n\t" 579 "mov %rsi, (%rsp) \n\t" 580 "movl $1, %eax \n\t" 581 "iretq \n\t" 582 ".section .text"); 583 584 asm volatile (".section .text.entry \n\t" 585 "kernel_entry: \n\t" 586 "mov %rdx, %rsp \n\t" 587 "jmp back_to_kernel \n\t" 588 ".section .text"); 589 590 ac_test_check(at, &success, fault && !at->expected_fault, 591 "unexpected fault"); 592 ac_test_check(at, &success, !fault && at->expected_fault, 593 "unexpected access"); 594 ac_test_check(at, &success, fault && e != at->expected_error, 595 "error code %x expected %x", e, at->expected_error); 596 ac_test_check(at, &success, at->ptep && *at->ptep != at->expected_pte, 597 "pte %x expected %x", *at->ptep, at->expected_pte); 598 ac_test_check(at, &success, 599 !pt_match(*at->pdep, at->expected_pde, at->ignore_pde), 600 "pde %x expected %x", *at->pdep, at->expected_pde); 601 602 if (success && verbose) { 603 printf("PASS\n"); 604 } 605 return success; 606 } 607 608 static void ac_test_show(ac_test_t *at) 609 { 610 char line[5000]; 611 612 *line = 0; 613 strcat(line, "test"); 614 for (int i = 0; i < NR_AC_FLAGS; ++i) 615 if (at->flags[i]) { 616 strcat(line, " "); 617 strcat(line, ac_names[i]); 618 } 619 strcat(line, ": "); 620 printf("%s", line); 621 } 622 623 /* 624 * This test case is used to triger the bug which is fixed by 625 * commit e09e90a5 in the kvm tree 626 */ 627 static int corrupt_hugepage_triger(ac_pool_t *pool) 628 { 629 ac_test_t at1, at2; 630 631 ac_test_init(&at1, (void *)(0x123400000000)); 632 ac_test_init(&at2, (void *)(0x666600000000)); 633 634 at2.flags[AC_CPU_CR0_WP] = 1; 635 at2.flags[AC_PDE_PSE] = 1; 636 at2.flags[AC_PDE_PRESENT] = 1; 637 ac_test_setup_pte(&at2, pool); 638 if (!ac_test_do_access(&at2)) 639 goto err; 640 641 at1.flags[AC_CPU_CR0_WP] = 1; 642 at1.flags[AC_PDE_PSE] = 1; 643 at1.flags[AC_PDE_WRITABLE] = 1; 644 at1.flags[AC_PDE_PRESENT] = 1; 645 ac_test_setup_pte(&at1, pool); 646 if (!ac_test_do_access(&at1)) 647 goto err; 648 649 at1.flags[AC_ACCESS_WRITE] = 1; 650 ac_set_expected_status(&at1); 651 if (!ac_test_do_access(&at1)) 652 goto err; 653 654 at2.flags[AC_ACCESS_WRITE] = 1; 655 ac_set_expected_status(&at2); 656 if (!ac_test_do_access(&at2)) 657 goto err; 658 659 return 1; 660 661 err: 662 printf("corrupt_hugepage_triger test fail\n"); 663 return 0; 664 } 665 666 int ac_test_exec(ac_test_t *at, ac_pool_t *pool) 667 { 668 int r; 669 670 if (verbose) { 671 ac_test_show(at); 672 } 673 ac_test_setup_pte(at, pool); 674 r = ac_test_do_access(at); 675 return r; 676 } 677 678 int ac_test_run(void) 679 { 680 ac_test_t at; 681 ac_pool_t pool; 682 int tests, successes; 683 684 printf("run\n"); 685 tests = successes = 0; 686 ac_env_int(&pool); 687 ac_test_init(&at, (void *)(0x123400000000 + 16 * smp_id())); 688 do { 689 ++tests; 690 successes += ac_test_exec(&at, &pool); 691 } while (ac_test_bump(&at)); 692 693 ++tests; 694 successes += corrupt_hugepage_triger(&pool); 695 696 printf("\n%d tests, %d failures\n", tests, tests - successes); 697 698 return successes == tests; 699 } 700 701 int main() 702 { 703 int r; 704 705 printf("starting test\n\n"); 706 r = ac_test_run(); 707 return r ? 0 : 1; 708 } 709