> On Aug 22, 2025, at 15:02, Kees Cook <k...@kernel.org> wrote:
> 
> On Fri, Aug 22, 2025 at 03:11:16PM +0000, Qing Zhao wrote:
>>> On Aug 21, 2025, at 17:29, Kees Cook <k...@kernel.org> wrote:
>>> For non-static functions, we cannot know if other compilation units may
>>> make indirect calls to a given function, so those functions must always
>>> have their kcfi preamble added. For static functions, if they are
>>> address-taken by the current compilation unit, then they must get a kcfi
>>> preamble added.
>> 
>> Oh, yeah, I see. without lto or whole-program-mode, we cannot determine 
>> whether a extern function is address taken or not. Therefore, we have to 
>> treat ALL extern functions conservatively as address taken. 
>> 
>> So, from my understanding, the complete list that need to compute the typeid 
>> from the function prototype is:
>> 
>> - At indirect call sites
>> - all indirect call sites; (At the call site)
>> - At function preambles
>> - all address-taken static functions  (At the function definition)
>> - all extern functions  (At function declaration or function definition?? 
>> Please see my question below)
> 
> For "extern functions", the logic is split as:
> - "all extern function definitions get preamble"
> - "all extern function declarations without a definition that are
>   address-taken get __kcfi_typeid_ symbol"

Okay. 
> 
>>> The other case is emitting the __ckfi_typeid_FUNC weak symbols, which is
>>> used for link-time resolution with non-C code (i.e. raw .S assembly)
>>> which doesn't have access to the C type system to calculate the hashes
>>> on its own, and needs to have a way to build its own kcfi preambles.
>> 
>> So, for such functions, there should be an extern function declaration in 
>> the C code. 
>> But the definition of such function is not available in the C code we are 
>> compiling. 
>> Therefore the weak __ckfi_typeid_FUNC symbol is emitted at the function 
>> declaration
>> point for such function when we compile the C code? 
>> 
>> And the typeid (the hash value) for such routine is computed at the function 
>> declaration 
>> point too. 
>> 
>> Is the above understanding correct?
> 
> Correct, the kcfi_typeid symbol and value are emitted at function
> declaration point, but only if such function is address-taken.
> 
>> Then for the other extern function whose definition is in the C code of 
>> other modules that might
>> be compiled later, should the typeid is computed at the declaration or the 
>> definition?
> 
> It is computed and emitted just for externs that are address-taken.

Okay. 
> 
>>> This
>>> is how Linux constructs its assembly function entry points:
>>> 
>>> #ifndef __CFI_TYPE
>>> #define __CFI_TYPE(name)                                \
>>>       .4byte __kcfi_typeid_##name
>>> #endif
>>> 
>>> #define SYM_TYPED_ENTRY(name, linkage, align...)        \
>>>       linkage(name) ASM_NL                            \
>>>       align ASM_NL                                    \
>>>       __CFI_TYPE(name) ASM_NL                         \
>>>       name:
>>> 
>>> That way all the asm functions can be be indirect call targets without
>>> knowing the hash value (which will be filled in at link time).
>> 
>> Okay. I see.  This is the case for the extern function whose definition is 
>> in the assembly file. (Not available in
>> the C code)
> 
> Right, and sometimes we have to explicitly perform a no-op
> address-taking to make sure a symbol gets generated:
> 
> /*
> * Force the compiler to emit 'sym' as a symbol, so that we can reference
> * it from inline assembler. Necessary in case 'sym' could be inlined
> * otherwise, or eliminated entirely due to lack of references that are
> * visible to the compiler.
> */
> #define ___ADDRESSABLE(sym, __attrs)                                          
>   \
>        static void * __used __attrs                                           
>  \
>        __UNIQUE_ID(__PASTE(__addressable_,sym)) = (void *)(uintptr_t)&sym;
> 
> #define __ADDRESSABLE(sym) \
>        ___ADDRESSABLE(sym, __section(".discard.addressable"))
> 
> $ git grep KCFI_REFERENCE
> include/linux/compiler.h:#define KCFI_REFERENCE(sym) __ADDRESSABLE(sym)
> arch/x86/include/asm/page_64.h:KCFI_REFERENCE(copy_page);
> arch/x86/include/asm/string_64.h:KCFI_REFERENCE(__memset);
> arch/x86/include/asm/string_64.h:KCFI_REFERENCE(__memmove);
> arch/x86/kernel/alternative.c:KCFI_REFERENCE(__bpf_prog_runX);
> arch/x86/kernel/alternative.c:KCFI_REFERENCE(__bpf_callback_fn);

I am curious on why the compiler eliminates an external routine completely in 
the file if it's address-taken in that file. 
Why an additional no-op address-taken is needed here. 
> 
> 
>>> I assume I just didn't see how yet. :) I wasn't able to identify nor
>>> store the typeid for function definitions that ultimately end up getting
>>> .s file output.
>> So, the problem only exists for the external functions whose definition is 
>> NOT in the C code?
> 
> Yup!
> 
>>> I couldn't figure out how to find these during the GIMPLE pass. Oh,
>>> perhaps I can do this with an IPA pass? That should let me walk all
>>> functions including externs. I'll give it a try...
> 
> Adding the IPA pass to find all functions worked perfectly. I was able
> to remove all the weird DECL reconstruction and just use the original
> FUNCTION_TYPE info for the typeids.

Nice. So this new IPA pass is invoked even when O0?

Qing

> 
> -Kees
> 
> -- 
> Kees Cook

Reply via email to