{"id":40923898,"url":"https://github.com/robertdfrench/ifuncd-up","last_synced_at":"2026-01-22T03:38:05.507Z","repository":{"id":246975724,"uuid":"824707729","full_name":"robertdfrench/ifuncd-up","owner":"robertdfrench","description":"GNU IFUNC is the real culprit behind CVE-2024-3094","archived":false,"fork":false,"pushed_at":"2024-07-25T19:17:59.000Z","size":3077,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"trunk","last_synced_at":"2024-07-25T23:12:14.527Z","etag":null,"topics":["cve-2024-3094","dynamic-linking","dynamic-loading","elf","glibc","global-offset-table","ifunc","memes","procedure-linkage-table","relro","ssh","supply-chain","systemd","xz-utils-backdoor"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/robertdfrench.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-07-05T18:36:16.000Z","updated_at":"2024-07-25T19:18:02.000Z","dependencies_parsed_at":"2024-07-06T02:17:50.876Z","dependency_job_id":"18f0ed09-14e9-4ead-a0d7-49e7cb8ae8ff","html_url":"https://github.com/robertdfrench/ifuncd-up","commit_stats":null,"previous_names":["robertdfrench/ifuncd-up"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/robertdfrench/ifuncd-up","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertdfrench%2Fifuncd-up","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertdfrench%2Fifuncd-up/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertdfrench%2Fifuncd-up/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertdfrench%2Fifuncd-up/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/robertdfrench","download_url":"https://codeload.github.com/robertdfrench/ifuncd-up/tar.gz/refs/heads/trunk","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertdfrench%2Fifuncd-up/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28653186,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-22T01:17:37.254Z","status":"online","status_checked_at":"2026-01-22T02:00:07.137Z","response_time":144,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cve-2024-3094","dynamic-linking","dynamic-loading","elf","glibc","global-offset-table","ifunc","memes","procedure-linkage-table","relro","ssh","supply-chain","systemd","xz-utils-backdoor"],"created_at":"2026-01-22T03:38:05.348Z","updated_at":"2026-01-22T03:38:05.496Z","avatar_url":"https://github.com/robertdfrench.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# IFUNC'd up\n*Why you should stop blaming xz-utils for [CVE-2024-3094][nvd]. Also\ncheck out my [ETSA Talk](https://www.youtube.com/watch?v=7j8ZCDGwSr8)!*\n\n![I think IFUNC'd up](memes/larry.jpeg)\n\nCVE-2024-3094, more commonly known as \"The xz-utils backdoor\", was a\nnear miss for global cybersecurity. Had this attack not been discovered\nin the nick of time by [Andres Freund][freund], most of our planet's SSH\nservers would have begun granting root access to the party behind this\nattack.\n\nUnfortunately, too much analysis has focused on how [malicious\ncode][JiaT75] made its way into the xz-utils repo.  Instead, I'd like to\nargue that two longstanding design decisions in critical open source\nsoftware are what made this attack possible: [linking OpenSSH against\nSystemD][biebl], and the existence of [GNU IFUNC][sourceware].\n\n**Before You Start**: Much of this discussion deals with the intricacies\nof dynamic linking on Linux. If you need a refresher, check out\n[`dynamic_linking.md`](dynamic_linking.md).\n \n\n\n## Quick Recap of CVE-2024-3094\nThere are tons of good writeups outlining the high level details of the\nxz-utils backdoor, like Dan Goodin's [What we know about the xz Utils\nbackdoor that almost infected the world][goodin1] and Sam James' [FAQ on\nthe xz-utils backdoor (CVE-2024-3094)][thesamesam] gist. We don't need\nto rehash all that here, so the purposes of this article, here is a\n**very coarse** recap:\n\n* Some Linux distros modify OpenSSH to depend on SystemD\n* SystemD depends on xz-utils, which uses GNU IFUNC\n* Ergo, xz-utils ends up in the address space of OpenSSH\n* This allows ifunc to modify code in the SSH server\n\n```mermaid\nflowchart TD\n    G[\"GNU IFUNC\"]\n    A[\"OpenSSH (OpenBSD)\"]\n    B[\"Portable OpenSSH\u003cbr/\u003e(Linux / macOS / etc)\"]\n    C[OpenSSH + IFUNC]\n    D[xz-utils]\n    E[\"SystemD (Linux)\"]\n    A --\u003e|Remove OpenBSD specifics| B\n    B --\u003e|Add SystemD specifics| C\n    D --\u003e E\n    E --\u003e C\n    C --\u003e F[\"Mayhem\"]\n    G --\u003e D\n```\n\n\n\n## Why do Linux Distros modify OpenSSH?\nThe short answer is that they have to. OpenSSH is developed by the\nOpenBSD community, for the OpenBSD community, and they do not give a\nflying Fedora about Linux.  The [Portable OpenSSH][mindrot] project is a\nbest-effort collection of patches which replace OpenBSD-specific\ncomponents with generic POSIX components, and some platform-specific\ncode where applicable. The software supply-chain for SSH ends up looking\nsomething like this in practice:\n\n```mermaid\nflowchart TD\n  subgraph OpenBSD Folks\n    A[OpenBSD]\n    B[OpenSSH]\n    H[improvements]\n  end\n  B--\u003eA\n  A--\u003eH\n  H--\u003eB\n\n  B--\u003eC\n  C[Portable OpenSSH]\n\n  subgraph Debian Folks\n    D[Debian SSH]\n    G[improvements]\n  end\n  C--\u003eD\n  D--\u003eG\n  G--\u003eC\n\n  subgraph Fedora Folks\n    J[Fedora SSH]\n    K[improvements]\n  end\n  C--\u003eJ\n  J--\u003eK\n  K--\u003eC\n```\n\nOpenBSD's version of OpenSSH is upstream from everything else, and most\nimprovements to it come from within the OpenBSD community. These changes\nflow downstream to the Portable OpenSSH project, which attempts to\nre-implement new features in ways that aren't specific to OpenBSD. This\nis what allows SSH to work on platforms like Linux, macOS, FreeBSD, and\neven Windows. \n\nBut it doesn't stop there. Some operating systems apply further\ncustomization beyond what Portable OpenSSH provides. For example, Apple\nadds the [`--apple-use-keychain`][keith] flag to `ssh-add` to help it\nintegrate with the macOS password manager.\n\nIn the case of CVE-2024-3094, Fedora and Debian maintained their own\n[SystemD patches][biebl] for their forks of OpenSSH in order to fix a\n[race condition around `sshd` restarts][schmidt]. So the *actual* supply\nchain for SSH began to look like this:\n\n```mermaid\nflowchart TD\n  A[OpenSSH]\n  B[Portable OpenSSH]\n  C[Debian SSH]\n  D[Fedora SSH]\n  A--\u003eB\n  B--\u003eC\n  B--\u003eD\n  C\u003c--\u003e|SystemD Patches|D\n```\n\nThese patches never went into Portable OpenSSH, because the Portable\nOpenSSH folks were [\"not interested in taking a dependency on\nlibsystemd\"][djmdjm]. And they never went into upstream OpenSSH, because\nOpenBSD doesn't have any need to support SystemD.\n\n\n\n### Concerns about \"Separation of Concerns\"\nThis seems harmless enough, but it's an example of a much larger problem\nin Open Source, particularly in Linux: critical components of the\noperating system are developed by people who don't know each other, and\ndon't talk to each other. \n\n* Did the folks who patched OpenSSH for SystemD know (or care) that\n  libsystemd depends on xz-utils?\n* Did the SystemD folks know (or care) that xz-utils had begun using\n  ifunc?\n* Did the OpenSSH folks know (or care) that ifunc was a thing? It's\n  certainly not a thing on OpenBSD.\n\nIn some sense, this breakdown in communication is a feature of open\nsource: I can adapt your work to my needs without having to bother you\nabout it. But it can also lead to a degree of indirection that prevents\ncritical design assumptions (such as a traditional dynamic linking\nprocess) from being upheld.\n\nThe obvious corollary to [Conway's Law][conway] is that if you are\nshipping your org chart, you're also shipping the bugs that live in the\ncracks of your org chart. No one person or team really made a mistake\nhere, but with the benefit of hindsight it's clear the attackers\nperceived that the left hand of Debian/Fedora SSH did not know what the\nright hand of xz-utils was doing.\n\n\n\n\n## What is GNU IFUNC *supposed* do?\nIt allows you to determine, at runtime, which version of some function\nyou'd like to use. It does this by giving you an opportunity to run\n**arbitrary code** to influence how the linker resolves symbols.\n\n![](memes/ifunc_hard_right.png)\n\n\n\n### Detecting CPU Features\nSuppose you have an application that must run on a wide variety of x86\nCPUs.  Depending on the specific features of the current CPU, you may\nprefer to use different algorithms for the same task. The original idea\nbehind IFUNC was to allow programs to check for CPU features the first\ntime a function is called, and thereafter use an implementation that\nwill be most appropriate for that CPU.\n\nTake a look at [`cpu_demo.c`](code/cpu_demo.c):\n\n```c\nvoid print_cpu_info() __attribute__((ifunc (\"resolve_cpu_info\")));\nvoid print_avx2() { printf(\"AVX2 is present.\\n\"); }\nvoid print_nope() { printf(\"AVX2 is missing.\\n\"); }\n\nstatic void* resolve_cpu_info(void) {\n    __builtin_cpu_init();\n\n\tif (__builtin_cpu_supports(\"avx2\")) {\n\t\treturn print_avx2;\n\t} else {\n\t\treturn print_nope;\n\t}\n}\n\nint main() {\n\tprint_cpu_info();\n\treturn 0;\n}\n```\n\nThis program shows the most common use of IFUNC: it asks the CPU whether\nor not it supports certain features, and provides a different\n*implementation* of a function depending on what features are supported.\nIn this case, our function `print_cpu_info` will end up printing either\n\"AVX2 is present\" or \"AVX2 is missing\" depending on how ancient your\nCPU is.\n\n\n\n### Probing the Process Environment\nWhile IFUNC is intended for probing CPU capabilities, nothing stops you\nfrom running more complicated code in your resolvers. For example,\n[`tty_demo.c`](code/tty_demo.c) shows how you can load a different\nfunction implementation depending on whether STDOUT is a file or a\nterminal: \n\n```c\n// Print Green text to the Terminal\nvoid print_to_tty(const char *message) {\n    const char *green_start = \"\\033[32m\";\n    const char *color_reset = \"\\033[0m\";\n    printf(\"%sTTY: %s%s\\n\", green_start, message, color_reset);\n}\n\n// Print plain text to a file\nvoid print_to_file(const char *message) {\n    printf(\"FILE: %s\\n\", message);\n}\n\nvoid print_message(const char *message) \\\n    __attribute__((ifunc(\"resolve_print_function\")));\n\nvoid (*resolve_print_function(void))(const char *) {\n    struct termios term;\n\n    // Ask the kernel whether stdout is a file or a tty\n    int result = ioctl(STDOUT_FILENO, TCGETS, \u0026term);\n    if (result == 0) {\n        // stdout is a terminal\n        return print_to_tty;\n    } else {\n        // stdout is not a terminal\n        return print_to_file;\n    }\n}\n\nint main() {\n    print_message(\"Hello, World!\");\n    return 0;\n}\n```\n\nThis is not really the intended use of IFUNC, but it shows what's\npossible: you can run arbitrary code before `main` in any program that\nuses an IFUNC that you've declared.\n\n\n\n\n\n## IFUNC is Probably a Bad Idea\n![](memes/ifunc_change_my_mind.png)\n\nGNU IFUNC is difficult to implement, hard to use correctly, and (as an\nalleged performance tool) isn't much faster than alternatives. As we've\nseen with CVE-2024-3094, it is also a very powerful tool for software\nsupply-chain attacks.\n\nIFUNC is used extensively within the GNU C Library, and that's probably\nfine.  Those are the folks for whom it was originally developed, and\nthey are closely connected with the compiler and linker teams who\nactually implement IFUNC. They are in the best position to understand\nthe tradeoffs, and there are tons of libc functions that benefit from\nCPU-specific implementations. I believe we should consider IFUNC to be\nan internal interface for glibc, and avoid its use in other\napplications.\n\n\n\n### It's too Confusing to Use Safely\nifunc is entirely too difficult to use. There are too many [corner\ncases][nagy], and the [official documentation][gnu-cfa] is\n[scant][sourceware]. This gives users the misleading idea that adopting\nifunc is straightforward.\n\nEven several years after ifunc became available, the advertised\ninterface [did not work][agner]. GCC developers have called it [a\nmistake][odonell] and have considered adding warnings to compensate for\nIFUNC's fragility:\n\n\u003e The glibc solutions required to make IFUNC robust are not in place,\n\u003e and so we should do what we can to warn users that it might break.\n\nIt isn't just IFUNC either. Apple Mach-O has a similar feature called\n`.symbol_resolver` which they [\"regret adding\"][rjmccall].\n\n\n\n### It Undermines RELRO\nBy allowing arbitrary code to run while the Global Offset Table is still\nwritable, protections afforded by [RELRO](dynamic_linking.md#relro) are\n[rendered moot][binarly-io]. \n\nThis is important to note, because RELRO advertises itself as a way to\nprotect the integrity of dynamically-loaded symbols. From a user\nperspective (you, as a user of the compiler and the linker), this\nviolates the [Principle of Least Astonishment][pola]: no reasonable\nperson would expect that *loading a dynamic library* should compromise\na safety feature designed to *protect dynamic libraries*.\n\n![](memes/linker_social_engineering.png)\n\n\n\n### It's Not Always Necessary\nThere are multiple other ways to handle this situation. They each have\ndifferent tradeoffs, but they are all far simpler than IFUNC. All of\nthese are more portable than IFUNC, easier to understand, and harder to\nexploit.\n\n\u003e \"Ifunc is just an utterly dumb way to do runtime microarch specific\n\u003e code selection.\"\n\u003e\n\u003e -- [Rich Felker](https://hachyderm.io/@dalias/112952237145378821),\n\u003e maintainer of [musl](https://musl.libc.org).\n\n\n#### Global Function Pointers\nIFUNC is attractive because it allows developers to express function\nselection *declaratively* rather than *imperatively*. But doing this\nimperatively is not actually that hard. Consider\n[`static_pointer.c`](code/static_pointer.c), which resolves a global\nfunction pointer at runtime:\n\n```c\nstatic int (*triple)(int) = 0;\nint triple_sse42(int n) { return 3 * n; }\nint triple_plain(int n) { return n + n + n; }\n\nvoid print_fifteen() {\n\tint fifteen = triple(5);\n\tprintf(\"%d\\n\", fifteen);\n}\n\nint main() {\n\t__builtin_cpu_init();\n\tif (__builtin_cpu_supports(\"sse4.2\")) {\n\t\ttriple = triple_sse42;\n\t} else {\n\t\ttriple = triple_plain;\n\t}\n\t\n\tprint_fifteen();\n\treturn 0;\n}\n```\n\nIs this really so bad that we need special gimmicks in the linker just\nto avoid it?\n\nOne disadvantage to this approach is that the function pointer `triple`\nis writable at runtime, whereas IFUNC+RELRO would ensure that the ifunc\naddresses in the GOT are immutable once they have been resolved.\nHowever, with a little extra legwork, we could use\n[`mprotect(2)`][mprotect] to mark such pointers read-only.\n\n\n\n#### Modifying `LD_PRELOAD`\nIf you know what CPU features your code needs, and you have a separate\ncopy of your dynamic library for each case, then you could accomplish\nthe same thing by specifying the right library with `$LD_PRELOAD` like\nso:\n\n```bash\n#!/bin/bash\nif (cat /proc/cpuinfo | grep flags | grep avx2 \u003e /dev/null); then\n\tLD_PRELOAD=./myfunc_avx2.so ./my_app\nelse\n\tLD_PRELOAD=./myfunc_normal.so ./my_app\nfi\n```\n\n(If you are unfamiliar with `LD_PRELOAD`, check out catonmat's [\"A Simple\n`LD_PRELOAD` Tutorial\"][catonmat].)\n\n\n\n#### Separate Binaries per Feature Combination\nHow many unique CPU feature combinations do you really need to support?\nHow many even exist?\n\nOn the face of it, this looks like a combinatorial explosion. There are\ndozens of different vector arithmetic, virtualization, and security\nextensions to the amd64 ISA. But these features do not occur\nindependently in the wild. For example, no CPU that has AVX-512 lacks\nSSE4.2 or AES-NI.\n\nKnowing which CPU features your application needs, and\nwhich of them occur together on real chips, can help you determine\nhow many distinct binaries you'd have to ship. It may not be as many as\nyou'd expect. Most package managers allow you to run scripts at install\ntime; you could ship multiple binaries in a single rpm or deb file and\nuse install-time logic to choose the best one for the host CPU.\n\n\n\n### It isn't Much Faster than Alternatives\n\n\u003e What's been obvious to me for a long time is that, even if there were\n\u003e a performance advantage to ifunc, it could only be when the entire\n\u003e function call is so short that call overhead can be a significant\n\u003e portion of overall time.\n\u003e\n\u003e -- [Rich Felker](https://hachyderm.io/@dalias/113074264762553873),\n\u003e maintainer of [musl](https://musl.libc.org).\n\nGiven that the usual justification for ifunc is performance-related, I\nwanted to see how much overhead *ifunc itself* causes. After all, any\nfunction worth optimizing is probably called frequently, so the overhead\nof the function invocation is worth acknowledging.\n\nTo figure this out, I designed an experiment that would call a\n*dynamically resolved* function over and over again in a tight loop.\nTake a look at [`speed_demo_ifunc.c`](code/speed_demo_ifunc.c) and\n[`speed_demo_pointer.c`](code/speed_demo_pointer.c).  These programs\nboth do the same work (incrementing a static counter), but the\nincrementer functions are resolved in different ways: the former\nleverages GNU IFUNC, and the latter relies on plain old function\npointers.\n\nHere is the overall logic:\n\n1. Call a resolver function to determine which incrementer to use.\n1. Record this answer somewhere (in the GOT, or as a function pointer).\n1. Call this incrementer function a few billion times to get an estimate\n   of its cost.\n\nAs a control, there is also\n[`speed_demo_fixed.c`](code/speed_demo_fixed.c) which does the same\nincrementer work but without any dynamically resolved functions.  This\ncan be used to get a help estimate what part of the runtime is dedicated\nto function invocation vs what part is just doing addition.\n\nThe Makefile target `rigorous_speed_demo` makes several runs of each of\nthese programs and produces some simple statistics about their\nperformance. These numbers will of course change based on your hardware,\nbut the `fixed` test should serve as a baseline for comparison.\n\n| *Results* | LOW  | HIGH | AVG   |\n|-----------|------|------|-------|\n| fixed     | 2.93 | 4.20 | 3.477 |\n| ifunc     | 9.50 | 10.56| 9.986 |\n| pointer   | 6.23 | 7.44 | 6.791 |\n\nWhat we see here is that ifunc has a not-insignificant overhead compared\nto using a plain-old function pointer. On average, on my hardware, it\ntakes about twice as long to call an ifunc function 2 billion times as\nit does to invoke a function pointer 2 billion times.\n\nDoes this matter in real life? Absolutely not. Functions that are worth\noptimizing are much more expensive than the \"increment by one\" functions\nthat we are analyzing here. It is only interesting because GNU IFUNC\nclaims to be a boon for performance, yet seems to incur more cost than\nfunction pointers.\n\n\n#### Performance of Other Techniques\nThere are other techniques which are slower than ifunc. Take a look at the\n`super_rigorous_speed_demo`, which brings to other experiments into play:\n[`speed_demo_upfront.c`](code/speed_demo_upfront.c) and\n[`speed_demo_always.c`](code/speed_demo_always.c).\n\n`speed_demo_upfront.c` behaves similarly to `speed_demo_pointer.c`,\nexcept that it stores the results of the cpu feature checks in global\nvariables rather than keeping track of a function pointer. This still\nrequires a \"resolver\" function to run first to determine which\nimplementation gets used, based on the value of these global variables.\nThis technique turns out to be slower than ifunc, but it is also safer\nthan storing function pointers: whereas function pointers can be set to\narbitrary values, boolean flags cannot. So an attacker able to modify\nthese variables can make the program *slower*, but cannot make the\nprogram behave *differently*.\n\n`speed_demo_always.c` is designed to be the slowest technique -- it\nchecks all the necessary CPU features every time an implementation is\nneeded and picks one on the fly. Curiously, this technique is not\nsignificantly slower than anything else. It is only marginally slower\nthan ifunc in the case where we have just a single CPU feature to check. \n\n| TEST    | LOW  | HIGH  | AVG     |\n|---------|------|-------|---------|\n| fixed   | 5.02 | 5.70  | 5.37    |\n| pointer | 6.40 | 7.02  | 6.66    |\n| ifunc   | 8.56 | 11.11 | 9.64    |\n| upfront | 9.24 | 9.41  | 9.33333 |\n| always  | 10.07| 10.56 | 10.2333 |\n\n\n\n\n## Conclusion\nGNU IFUNC is a niche feature of gcc/ld.so that few people knew about\nbefore it was used in CVE-2024-3094. It has non-obvious pitfalls and\ninsufficient documentation. By letting the linker run arbitrary code\nbefore `main`, before critical parts of the process image have been\ninitialized and protected, it undermines one of the most basic\nassumptions of programming: that the mere act of loading a library will\nnot inherently *change* your program.\n\nThe performance benefits of IFUNC are real, but not meaningfully better\nthan alternatives. The simplicity of deploying a single binary that is\noptimized for multiple CPUs is very attractive, but it can be\naccomplished with simpler techniques (like function pointers).\n\nI believe IFUNC should be disabled by default in gcc. Enabling it should\nrequire a scary looking flag, such as `--enable-cve-2024-3094`. Anyone\nusing it outside of libc should be expected to provide a rigorous,\nwell-researched argument that no alternative solution is appropriate.\n\n![Yes, all shared libraries](memes/brain.png)\n\n\u003c!-- REFERENCES --\u003e\n[aes-ni]: https://en.wikipedia.org/wiki/AES_instruction_set\n[agner]: https://www.agner.org/optimize/blog/read.php?i=167\n[biebl]: https://salsa.debian.org/ssh-team/openssh/-/commit/818791ef8edf087481bd49eb32335c8d7e1953d6\n[binarly-io]: https://github.com/binarly-io/binary-risk-intelligence/tree/master/xz-backdoor\n[catonmat]: https://catonmat.net/simple-ld-preload-tutorial\n[conway]: https://en.wikipedia.org/wiki/Conway's_law\n[djmdjm]: https://github.com/openssh/openssh-portable/pull/251#issuecomment-2027935208\n[fr0gger]: https://infosec.exchange/@fr0gger/112189232773640259\n[freund]: https://www.openwall.com/lists/oss-security/2024/03/29/4\n[gnu-cfa]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-ifunc-function-attribute\n[goodin1]: https://arstechnica.com/security/2024/04/what-we-know-about-the-xz-utils-backdoor-that-almost-infected-the-world/\n[jasoncc]: https://jasoncc.github.io/gnu_gcc_glibc/gnu-ifunc.html#relocations-and-pic\n[JiaT75]: https://github.com/tukaani-project/xz/commit/cf44e4b7f5dfdbf8c78aef377c10f71e274f63c0\n[keith]: https://keith.github.io/xcode-man-pages/ssh-add.1.html#apple-use-keychain\n[mindrot]: https://anongit.mindrot.org/openssh.git\n[mprotect]: https://www.man7.org/linux/man-pages/man2/mprotect.2.html\n[musl]: https://musl.libc.org\n[nagy]: https://sourceware.org/legacy-ml/libc-alpha/2015-11/msg00108.html\n[nvd]: https://nvd.nist.gov/vuln/detail/CVE-2024-3094\n[odonell]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70082#c0\n[OpenSSH9.8p1]: https://www.openssh.com/releasenotes.html#9.8p1\n[openssh-unix-dev]: https://marc.info/?l=openssh-unix-dev\u0026m=171288895109872\u0026w=2\n[pola]: https://en.wikipedia.org/wiki/Principle_of_least_astonishment\n[rjmccall]: https://reviews.llvm.org/D139163#3993795\n[schmidt]: https://bugzilla.redhat.com/show_bug.cgi?id=1381997#c4\n[sourceware]: https://sourceware.org/glibc/wiki/GNU_IFUNC\n[thesamesam]: https://gist.github.com/thesamesam/223949d5a074ebc3dce9ee78baad9e27#design\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobertdfrench%2Fifuncd-up","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobertdfrench%2Fifuncd-up","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobertdfrench%2Fifuncd-up/lists"}