this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at fixPythonPipStalling 731 lines 18 kB view raw
1/* 2 * Copyright (c) 2011-2015, 2017, 2018 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <arpa/inet.h> 25#include <assert.h> 26#include <notify.h> 27#include <string.h> 28#include <sys/socket.h> 29#include <stdlib.h> 30#include <stdbool.h> 31#include "network_state_information_priv.h" 32#include <limits.h> 33#include <stdio.h> 34#include <syslog.h> 35 36#define NWI_IFSTATE_FLAGS(flags) \ 37 ((flags) & NWI_IFSTATE_FLAGS_MASK) 38 39#define NWI_IFSTATE_FLAGS_GET_DIFF(flags) \ 40 (((flags) & NWI_IFSTATE_FLAGS_DIFF_MASK) >> 8) 41 42#define NWI_IFSTATE_FLAGS_FROM_DIFF(diff) \ 43 (((diff) << 8) & NWI_IFSTATE_FLAGS_DIFF_MASK) 44 45#define NWI_IFSTATE_DIFF_UNCHANGED 0 46#define NWI_IFSTATE_DIFF_ADDED 1 47#define NWI_IFSTATE_DIFF_REMOVED 2 48#define NWI_IFSTATE_DIFF_CHANGED 3 49#define NWI_IFSTATE_DIFF_RANK_UP 4 50#define NWI_IFSTATE_DIFF_RANK_DOWN 5 51 52 53static void 54nwi_state_fix_af_aliases(nwi_state_t state, uint32_t old_max_if_count) 55{ 56 int64_t offset; 57 int i; 58 nwi_ifstate_t ifstate; 59 60 offset = state->max_if_count - old_max_if_count; 61 if (offset < 0) { 62 syslog(LOG_ERR, "new count %d < old count %d", 63 state->max_if_count, old_max_if_count); 64 return; 65 } 66 67 /* iterate IPv4 list and add the offset to any entries with an alias */ 68 for (i = 0, ifstate = nwi_state_ifstate_list(state, AF_INET); 69 i < state->ipv4_count; 70 i++, ifstate++) { 71 if (ifstate->af_alias_offset != 0) { 72 /* IPv6 is higher in memory, alias is forward */ 73 ifstate->af_alias_offset += offset; 74 } 75 } 76 77 /* iterate IPv6 list and add the offset to any entries with an alias */ 78 for (i = 0, ifstate = nwi_state_ifstate_list(state, AF_INET6); 79 i < state->ipv6_count; 80 i++, ifstate++) { 81 if (ifstate->af_alias_offset != 0) { 82 /* IPv6 is higher in memory, alias is backward */ 83 ifstate->af_alias_offset -= offset; 84 } 85 } 86 return; 87} 88 89static void 90nwi_state_add_to_if_list(nwi_state_t state, nwi_ifstate_t ifstate) 91{ 92 int i; 93 nwi_ifindex_t * scan; 94 95 if ((ifstate->flags & NWI_IFSTATE_FLAGS_NOT_IN_IFLIST) != 0) { 96 /* doesn't get added to interface list */ 97 return; 98 } 99 if (state->if_list_count >= state->max_if_count) { 100 /* sanity check */ 101 return; 102 } 103 for (i = 0, scan = nwi_state_if_list(state); 104 i < state->if_list_count; 105 i++, scan++) { 106 nwi_ifstate_t this_ifstate; 107 108 this_ifstate = state->ifstate_list + *scan; 109 if (strcmp(this_ifstate->ifname, ifstate->ifname) == 0) { 110 /* it's already in the list */ 111 return; 112 } 113 } 114 /* add it to the end */ 115 *scan = (nwi_ifindex_t)(ifstate - state->ifstate_list); 116 state->if_list_count++; 117 return; 118} 119 120static void 121nwi_state_set_if_list(nwi_state_t state) 122{ 123 nwi_ifstate_t scan_v4; 124 nwi_ifstate_t scan_v6; 125 int v4; 126 int v6; 127 128 v4 = 0; 129 v6 = 0; 130 state->if_list_count = 0; 131 scan_v4 = nwi_state_get_ifstate_with_index(state, AF_INET, v4); 132 scan_v6 = nwi_state_get_ifstate_with_index(state, AF_INET6, v6); 133 while (scan_v4 != NULL || scan_v6 != NULL) { 134 boolean_t add_v4 = FALSE; 135 136 if (scan_v4 != NULL && scan_v6 != NULL) { 137 /* add the higher rank of v4 or v6 */ 138 if (scan_v4->rank <= scan_v6->rank) { 139 add_v4 = TRUE; 140 } 141 } 142 else if (scan_v4 != NULL) { 143 add_v4 = TRUE; 144 } 145 if (add_v4) { 146 /* add v4 interface */ 147 nwi_state_add_to_if_list(state, scan_v4); 148 v4++; 149 scan_v4 = nwi_state_get_ifstate_with_index(state, 150 AF_INET, 151 v4); 152 } 153 else { 154 /* add v6 interface, move to next item */ 155 nwi_state_add_to_if_list(state, scan_v6); 156 v6++; 157 scan_v6 = nwi_state_get_ifstate_with_index(state, 158 AF_INET6, 159 v6); 160 } 161 } 162 return; 163} 164 165__private_extern__ 166nwi_state_t 167nwi_state_make_copy(nwi_state_t src) 168{ 169 nwi_state_t dest = NULL; 170 size_t size; 171 172 if (src == NULL) { 173 return dest; 174 } 175 size = nwi_state_size(src); 176 dest = (nwi_state_t)malloc(size); 177 178 if (dest != NULL) { 179 memcpy(dest, src, size); 180 } 181 return dest; 182} 183 184__private_extern__ 185nwi_state_t 186nwi_state_new(nwi_state_t old_state, int max_if_count) 187{ 188 size_t size; 189 nwi_state_t state = NULL; 190 191 if (old_state == NULL && max_if_count == 0) { 192 return NULL; 193 } 194 195 /* Should we reallocate? */ 196 if (old_state != NULL) { 197 if (old_state->max_if_count >= max_if_count) { 198 /* if we're staying the same or shrinking, don't grow */ 199 return (old_state); 200 } 201 } 202 size = nwi_state_compute_size(max_if_count); 203 state = (nwi_state_t)malloc(size); 204 memset(state, 0, size); 205 state->max_if_count = max_if_count; 206 state->version = NWI_STATE_VERSION; 207 208 if (old_state != NULL) { 209 state->ipv6_count = old_state->ipv6_count; 210 if (state->ipv6_count > 0) { 211 memcpy((void *)&state->ifstate_list[state->max_if_count], 212 (void *)&old_state->ifstate_list[old_state->max_if_count], 213 old_state->ipv6_count * sizeof(nwi_ifstate)); 214 } 215 216 state->ipv4_count = old_state->ipv4_count; 217 if (state->ipv4_count > 0) { 218 memcpy((void *)state->ifstate_list, 219 (void *)old_state->ifstate_list, 220 old_state->ipv4_count * sizeof(nwi_ifstate)); 221 } 222 /* we grew the arrays so re-compute the offsets */ 223 nwi_state_fix_af_aliases(state, old_state->max_if_count); 224 nwi_state_set_if_list(state); 225 nwi_state_free(old_state); 226 } else { 227 state->ipv4_count = 0; 228 state->ipv6_count = 0; 229 } 230 return state; 231} 232 233__private_extern__ void 234nwi_state_finalize(nwi_state_t state) 235{ 236 if (state == NULL) { 237 return; 238 } 239 nwi_state_set_if_list(state); 240 return; 241} 242 243static __inline__ 244nwi_ifstate_t 245nwi_state_get_last_ifstate(nwi_state_t state, int af, nwi_ifindex_t** last) 246{ 247 nwi_ifindex_t * count; 248 int idx; 249 250 assert(state != NULL); 251 252 count = (af == AF_INET) ? &state->ipv4_count 253 : &state->ipv6_count; 254 255 idx = (af == AF_INET) ? state->ipv4_count 256 : (state->max_if_count + state->ipv6_count); 257 258 *last = count; 259 260 return &state->ifstate_list[idx]; 261} 262 263__private_extern__ 264void 265nwi_ifstate_set_signature(nwi_ifstate_t ifstate, uint8_t * signature) 266{ 267 memcpy(ifstate->signature, signature, sizeof(ifstate->signature)); 268 ifstate->flags |= NWI_IFSTATE_FLAGS_HAS_SIGNATURE; 269 return; 270} 271 272static void 273nwi_state_add_ifstate_alias(nwi_state_t state, nwi_ifstate_t ifstate) 274{ 275 nwi_ifstate_t alias; 276 277 alias = nwi_state_get_ifstate_with_name(state, 278 nwi_other_af(ifstate->af), 279 ifstate->ifname); 280 if (alias == NULL) { 281 return; 282 } 283 ifstate->af_alias_offset = (nwi_ifindex_t)(alias - ifstate); 284 alias->af_alias_offset = (nwi_ifindex_t)(ifstate - alias); 285 return; 286} 287 288__private_extern__ nwi_ifstate_t 289nwi_state_add_ifstate(nwi_state_t state, 290 const char * ifname, int af, 291 uint64_t flags, Rank rank, 292 void * ifa, 293 struct sockaddr * vpn_server_addr, 294 uint32_t reach_flags) 295{ 296 nwi_ifstate_t ifstate; 297 298 /* Will only add unique elements to the list */ 299 ifstate = nwi_state_get_ifstate_with_name(state, af, ifname); 300 301 /* Already present, just ignore it */ 302 if (ifstate != NULL) { 303 if (ifstate->rank < rank) { 304 /* always true because they are added in order */ 305 return NULL; 306 } 307 } 308 else { 309 /* add to the end */ 310 nwi_ifindex_t count; 311 nwi_ifindex_t * count_p; 312 313 /* make sure we aren't already full */ 314 ifstate = nwi_state_get_last_ifstate(state, af, &count_p); 315 count = *count_p; 316 if (count == state->max_if_count) { 317 /* should not happen */ 318 syslog(LOG_ERR, 319 "nwi_state_add_ifstate: full at count %d\n", 320 count); 321 return (NULL); 322 } 323 if (count > 0) { 324 /* previous ifstate is no longer last */ 325 nwi_ifstate_t prev = ifstate - 1; 326 327 prev->flags &= ~NWI_IFSTATE_FLAGS_LAST_ITEM; 328 } 329 memset(ifstate, 0, sizeof(*ifstate)); 330 strlcpy(ifstate->ifname, ifname, sizeof(ifstate->ifname)); 331 ifstate->af = af; 332 /* this is the new last ifstate */ 333 ifstate->flags |= NWI_IFSTATE_FLAGS_LAST_ITEM; 334 (*count_p)++; 335 336 /* add the alias */ 337 nwi_state_add_ifstate_alias(state, ifstate); 338 } 339 340 /* We need to update the address/rank/flag fields for the existing/new 341 * element */ 342 if (ifa != NULL) { 343 switch (af) { 344 case AF_INET: 345 ifstate->iaddr = *((struct in_addr *) ifa); 346 break; 347 case AF_INET6: 348 ifstate->iaddr6 = *((struct in6_addr *) ifa); 349 break; 350 default: 351 break; 352 } 353 354 } 355 356 if (vpn_server_addr != NULL && vpn_server_addr->sa_family != 0) { 357 _nwi_ifstate_set_vpn_server(ifstate, vpn_server_addr); 358 } else { 359 _nwi_ifstate_set_vpn_server(ifstate, NULL); 360 } 361 362 ifstate->reach_flags = reach_flags; 363 ifstate->rank = rank; 364 ifstate->flags &= ~NWI_IFSTATE_FLAGS_MASK; 365 ifstate->flags |= NWI_IFSTATE_FLAGS(flags); 366 return ifstate; 367} 368 369__private_extern__ 370void 371nwi_state_clear(nwi_state_t state, int af) 372{ 373 if (af == AF_INET) { 374 state->ipv4_count = 0; 375 } 376 else { 377 state->ipv6_count = 0; 378 } 379 return; 380 381} 382 383__private_extern__ 384void * 385nwi_ifstate_get_address(nwi_ifstate_t ifstate) 386{ 387 return (void *)&ifstate->iaddr; 388} 389 390 391static __inline__ uint8_t 392nwi_ifstate_get_diff(nwi_ifstate_t ifstate) 393{ 394 return (NWI_IFSTATE_FLAGS_GET_DIFF(ifstate->flags)); 395} 396 397__private_extern__ const char * 398nwi_ifstate_get_diff_str(nwi_ifstate_t ifstate) 399{ 400 const char * strings[] = { 401 "", 402 "+", 403 "-", 404 "!", 405 "/", 406 "\\" 407 }; 408 uint8_t diff; 409 410 diff = nwi_ifstate_get_diff(ifstate); 411 if (diff < sizeof(strings) / sizeof(strings[0])) { 412 return (strings[diff]); 413 } 414 return ("?"); 415} 416 417__private_extern__ nwi_ifstate_difference_t 418nwi_ifstate_get_difference(nwi_ifstate_t diff_ifstate) 419{ 420 nwi_ifstate_difference_t diff; 421 422 switch (nwi_ifstate_get_diff(diff_ifstate)) { 423 case NWI_IFSTATE_DIFF_ADDED: 424 case NWI_IFSTATE_DIFF_CHANGED: 425 diff = knwi_ifstate_difference_changed; 426 break; 427 case NWI_IFSTATE_DIFF_REMOVED: 428 diff = knwi_ifstate_difference_removed; 429 break; 430 default: 431 diff = knwi_ifstate_difference_none; 432 break; 433 } 434 return (diff); 435} 436 437static inline boolean_t 438nwi_ifstate_has_changed(nwi_ifstate_t ifstate1, nwi_ifstate_t ifstate2) 439{ 440 if (NWI_IFSTATE_FLAGS(ifstate1->flags) 441 != NWI_IFSTATE_FLAGS(ifstate2->flags)) { 442 return TRUE; 443 } 444 445 if (ifstate1->af == AF_INET) { 446 if (memcmp(&ifstate1->iaddr, &ifstate2->iaddr, sizeof(struct in_addr)) != 0) { 447 return TRUE; 448 } 449 } else { 450 if (memcmp(&ifstate1->iaddr6, &ifstate2->iaddr6, sizeof(struct in6_addr)) != 0) { 451 return TRUE; 452 } 453 } 454 return FALSE; 455} 456 457static inline nwi_ifstate_t 458nwi_state_diff_append(nwi_state_t state, nwi_ifstate_t scan) 459{ 460 nwi_ifindex_t * last; 461 nwi_ifstate_t new_ifstate = NULL; 462 463 new_ifstate = nwi_state_get_last_ifstate(state, scan->af, &last); 464 memcpy(new_ifstate, scan, sizeof(*scan)); 465 (*last)++; 466 return new_ifstate; 467} 468 469static inline void 470nwi_ifstate_set_diff(nwi_ifstate_t ifstate, uint8_t diff) 471{ 472 ifstate->flags &= ~NWI_IFSTATE_FLAGS_DIFF_MASK; 473 if (diff != NWI_IFSTATE_DIFF_UNCHANGED) { 474 ifstate->flags |= NWI_IFSTATE_FLAGS_FROM_DIFF(diff); 475 } 476} 477 478static void 479nwi_state_diff_add_change(nwi_state_t diff, nwi_state_t old_ifstate, 480 nwi_ifstate_t ifstate) 481{ 482 nwi_ifstate_t existing; 483 nwi_ifstate_t new_ifstate; 484 485 existing = nwi_state_get_ifstate_with_name(old_ifstate, 486 ifstate->af, 487 nwi_ifstate_get_ifname(ifstate)); 488 new_ifstate = nwi_state_diff_append(diff, ifstate); 489 if (existing != NULL) { 490 if (nwi_ifstate_has_changed(existing, new_ifstate)) { 491 nwi_ifstate_set_diff(new_ifstate, 492 NWI_IFSTATE_DIFF_CHANGED); 493 } else if (existing->rank < new_ifstate->rank) { 494 nwi_ifstate_set_diff(new_ifstate, 495 NWI_IFSTATE_DIFF_RANK_DOWN); 496 } else if (existing->rank > new_ifstate->rank) { 497 nwi_ifstate_set_diff(new_ifstate, 498 NWI_IFSTATE_DIFF_RANK_UP); 499 } else { 500 nwi_ifstate_set_diff(new_ifstate, 501 NWI_IFSTATE_DIFF_UNCHANGED); 502 } 503 } else { 504 nwi_ifstate_set_diff(new_ifstate, NWI_IFSTATE_DIFF_ADDED); 505 } 506 return; 507} 508 509static void 510nwi_state_diff_remove(nwi_state_t state, nwi_ifstate_t ifstate) 511{ 512 nwi_ifstate_t removed_ifstate; 513 514 if (nwi_state_get_ifstate_with_name(state, 515 ifstate->af, 516 nwi_ifstate_get_ifname(ifstate)) 517 != NULL) { 518 /* there's still an ifstate */ 519 return; 520 } 521 removed_ifstate = nwi_state_diff_append(state, ifstate); 522 nwi_ifstate_set_diff(removed_ifstate, NWI_IFSTATE_DIFF_REMOVED); 523 return; 524} 525 526static void 527nwi_state_diff_populate(nwi_state_t diff, nwi_state_t old_ifstate, nwi_state_t new_ifstate) 528{ 529 int i; 530 nwi_ifstate_t scan; 531 532 if (new_ifstate != NULL) { 533 /* check for adds/changes */ 534 if (new_ifstate->ipv4_count) { 535 for (i = 0, scan = new_ifstate->ifstate_list; 536 i < new_ifstate->ipv4_count; i++, scan++) { 537 nwi_state_diff_add_change(diff, old_ifstate, scan); 538 } 539 } 540 if (new_ifstate->ipv6_count) { 541 scan = new_ifstate->ifstate_list + new_ifstate->max_if_count; 542 for (i = 0; 543 i < new_ifstate->ipv6_count; i++, scan++) { 544 nwi_state_diff_add_change(diff, old_ifstate, scan); 545 } 546 } 547 } 548 if (old_ifstate != NULL) { 549 /* check for removes */ 550 if (old_ifstate->ipv4_count) { 551 for (i = 0, scan = old_ifstate->ifstate_list; 552 i < old_ifstate->ipv4_count; i++, scan++) { 553 nwi_state_diff_remove(diff, scan); 554 } 555 } 556 if (old_ifstate->ipv6_count) { 557 scan = old_ifstate->ifstate_list + old_ifstate->max_if_count; 558 for (i = 0; 559 i < old_ifstate->ipv6_count; i++, scan++) { 560 nwi_state_diff_remove(diff, scan); 561 } 562 } 563 } 564 return; 565} 566 567static int 568nwi_state_max_af_count(nwi_state_t state) 569{ 570 if (state->ipv4_count >= state->ipv6_count) { 571 return (state->ipv4_count); 572 } 573 return (state->ipv6_count); 574} 575 576__private_extern__ nwi_state_t 577nwi_state_diff(nwi_state_t old_ifstate, nwi_state_t new_ifstate) 578{ 579 nwi_state_t diff; 580 int total_count = 0; 581 582 /* 583 * Compute the worst case total number of elements we need: 584 * the max count of (IPv4, IPv6) in the old 585 * + the max count of (IPv4, IPv6) in the new 586 * Worst case assumes that the old and new share none of the 587 * same interfaces. 588 */ 589 if (old_ifstate != NULL) { 590 total_count += nwi_state_max_af_count(old_ifstate); 591 } 592 if (new_ifstate != NULL) { 593 total_count += nwi_state_max_af_count(new_ifstate); 594 } 595 if (total_count == 0) { 596 return NULL; 597 } 598 599 diff = nwi_state_new(NULL, total_count); 600 nwi_state_diff_populate(diff, old_ifstate, new_ifstate); 601 602 /* diff consists of a nwi_state_t with diff flags on each ifstate */ 603 return diff; 604} 605 606static __inline__ 607void 608_nwi_ifstate_set_generation(nwi_ifstate_t ifstate, uint64_t generation_count) 609{ 610 ifstate->if_generation_count = generation_count; 611 612 return; 613} 614 615static 616boolean_t 617_nwi_ifstate_has_changed(nwi_state_t state, const char * ifname) 618{ 619 nwi_ifstate_t ifstate; 620 621 /* If either the v4 ifstate or the v6 ifstate 622 * has changed, then report that the interface has changed */ 623 ifstate = nwi_state_get_ifstate_with_name(state, 624 AF_INET, 625 ifname); 626 627 if (ifstate != NULL 628 && nwi_ifstate_get_diff(ifstate) != NWI_IFSTATE_DIFF_UNCHANGED) { 629 return (TRUE); 630 } 631 632 ifstate = nwi_state_get_ifstate_with_name(state, 633 AF_INET6, 634 ifname); 635 636 if (ifstate != NULL 637 && nwi_ifstate_get_diff(ifstate) != NWI_IFSTATE_DIFF_UNCHANGED) { 638 return (TRUE); 639 } 640 return (FALSE); 641} 642 643__private_extern__ 644void 645_nwi_state_update_interface_generations(nwi_state_t old_state, nwi_state_t state, nwi_state_t changes) 646{ 647 int i; 648 uint64_t generation_count; 649 nwi_ifstate_t scan; 650 651 if (state == NULL || changes == NULL) { 652 return; 653 } 654 655 /* cache the generation count */ 656 generation_count = state->generation_count; 657 658 for (i = 0, scan = nwi_state_ifstate_list(state, AF_INET); 659 i < state->ipv4_count; i++, scan++) { 660 if (_nwi_ifstate_has_changed(changes, scan->ifname)) { 661 /* Update the interface generation count */ 662 _nwi_ifstate_set_generation(scan, generation_count); 663 } else { 664 nwi_ifstate_t old_ifstate; 665 666 old_ifstate = nwi_state_get_ifstate_with_name(old_state, 667 AF_INET, 668 scan->ifname); 669 assert(old_ifstate != NULL); 670 671 /* Set the current generation count */ 672 _nwi_ifstate_set_generation(scan, 673 old_ifstate->if_generation_count); 674 } 675 } 676 for (i = 0, scan = nwi_state_ifstate_list(state, AF_INET6); 677 i < state->ipv6_count; i++, scan++) { 678 /* The generation count has been already updated in 679 * the ipv4 case, just skip it. */ 680 if (nwi_ifstate_get_generation(scan) == 681 generation_count) { 682 continue; 683 } 684 if (_nwi_ifstate_has_changed(changes, scan->ifname)) { 685 /* update the interface generation count */ 686 _nwi_ifstate_set_generation(scan, generation_count); 687 } else { 688 nwi_ifstate_t old_ifstate; 689 690 old_ifstate = nwi_state_get_ifstate_with_name(old_state, 691 AF_INET6, 692 scan->ifname); 693 assert(old_ifstate != NULL); 694 695 /* Set the current generation count */ 696 _nwi_ifstate_set_generation(scan, 697 old_ifstate->if_generation_count); 698 } 699 } 700 return; 701} 702 703__private_extern__ 704void 705_nwi_state_compute_sha256_hash(nwi_state_t state, 706 unsigned char hash[CC_SHA256_DIGEST_LENGTH]) 707{ 708 CC_SHA256_CTX ctx; 709 uint64_t generation_save; 710 711 if (state == NULL) { 712 memset(hash, 0, CC_SHA256_DIGEST_LENGTH); 713 return; 714 } 715 716 /* preserve generation */ 717 generation_save = state->generation_count; 718 719 /* zero out the generation count before computing hash */ 720 state->generation_count = 0; 721 722 /* compute hash */ 723 CC_SHA256_Init(&ctx); 724 CC_SHA256_Update(&ctx, state, (CC_LONG)nwi_state_size(state)); 725 CC_SHA256_Final(hash, &ctx); 726 727 /* restore generation */ 728 state->generation_count = generation_save; 729 730 return; 731}