On Tue, Sep 09, 2025 at 06:49:22PM +0000, Qing Zhao wrote:
> 
> > On Sep 4, 2025, at 20:24, Kees Cook <k...@kernel.org> wrote:
> > +For indirect call sites:
> > +
> > +- Keeping indirect calls from being merged (see above) by adding a
> > +  wrapping type so that equality was tested based on type-id.
> 
> I still think that the additional new created wrapping type and the new 
> assignment stmt
> 
> wrapper_tmp = (wrapper_ptr_type) fn
> is not necessary.
> 
> All the information can be get from function type + type-id which is attached 
> as an attribute
> to the original_function_type of “fn”. 
> Could you explain why the wrapper type and the new temporary, new assignment 
> is 
> necessary?

I couldn't find a way to stop merging just using the attributes. I need
a way to directly associated indirect call sites with the typeid.

> > +
> > +- Keeping typeid information available through to the RTL expansion
> > +  phase was done via a new KCFI insn that wraps CALL and the typeid.
> 
> Is the new KCFI insn the following:
> wrapper_tmp = (wrapper_ptr_type) fn?

This bullet is speaking about the backend change to support the KCFI
check-call insn sequences:

+/* KCFI wrapper for call expressions.
+   Operand 0 is the call expression.
+   Operand 1 is the KCFI type ID (const_int).  */
+
+DEF_RTL_EXPR(KCFI, "kcfi", "ee", RTX_EXTRA)

> Why the type-id attached as the attribute is not enough? 

Doing the wrapping avoided needing to update multiple optimization passes
to check for the attribute. And it still needed a way to distinguish
between direct and indirect calls, so I need to wrap only the indirect
calls, where as the typeid attribute is for all functions for all typeid
needs, like preamble generation, etc.

> > +
> > +- To make sure KCFI expansion is skipped for inline functions, the
> > +  inlining is marked during GIMPLE with a new flag which is checked
> > +  during expansion.
> > +
> > +For indirect call targets:
> > +
> > +- kcfi_emit_preamble() uses function_needs_kcfi_preamble(),
> > +  to emit the preablem,
> Typo: preamble. 

Fixed.

> > +HOST_WIDE_INT kcfi_patchable_entry_prefix_nops = 0;  /* For callsite 
> > offset */
> > +static HOST_WIDE_INT kcfi_patchable_entry_arch_alignment_nops = 0;  /* For 
> > preamble alignment */
> > +
> > +/* Common helper for RTL patterns to emit .kcfi_traps section entry.  */
> 
> there should be one empty line between the comments of the function and the 
> start of the function. 
> I noticed that you need such empty line for all the new functions you added.  
> -:)

Oh! Wow, yeah, I totally missed this coding style requirement. Fixed.

> > +/* Check if a function needs KCFI preamble generation.
> > +   ALL functions get preambles when -fsanitize=kcfi is enabled, regardless
> > +   of no_sanitize("kcfi") attribute.  */
> 
> Why no_sanitize(“kcfi”) is not considered here?

no_sanitize(“kcfi”) is strictly about whether call-site checking
is performed within the function. It is not used to mark a function as
not being the target of a KCFI call.

> > +/* Get KCFI type ID for a function declaration during assembly output 
> > phase.
> > +   Fatal error if type ID was not previously set during IPA phase.  */
> > +static uint32_t
> > +get_function_kcfi_type_id (tree fndecl)
> > +{
> > +  if (!kcfi_type_id_attr)
> > +    kcfi_type_id_attr = get_identifier ("kcfi_type_id");
> > +
> > +  tree attr = lookup_attribute_by_prefix ("kcfi_type_id",
> > +   DECL_ATTRIBUTES (fndecl));
> > +  if (attr && TREE_VALUE (attr) && TREE_VALUE (TREE_VALUE (attr)))
> > +    {
> > +      tree value = TREE_VALUE (TREE_VALUE (attr));
> > +      if (TREE_CODE (value) == INTEGER_CST)
> > + return (uint32_t) TREE_INT_CST_LOW (value);
> The indentation of above is off.

I understand GCC code style indentation to be "leading spans of 8 spaces
should be replaced with a tab character". This is what I followed:

first line indents to column 6, so 6 spaces. Second line indents to
column 8, so 1 tab:

SSSSSSif (TREE_CODE (value) == INTEGER_CST)
Treturn (uint32_t) TREE_INT_CST_LOW (value);

This seems to match everywhere else? Randomly picking line get_section()
from varasm.cc, I see the same:

      if (not_existing)
        internal_error ("section already exists: %qs", name);

> > +  /* Calculate alignment NOPs needed */
> > +  arch_alignment = (alignment_bytes - ((kcfi_patchable_entry_prefix_nops + 
> > typeid_size) % alignment_bytes)) % alignment_bytes;
> The above line is too long. 

Oops, yes, thank you. Fixed.

What is the right tool for me to run to check for these kinds of code
style glitches? contrib/check_GNU_style.py doesn't report anything. Oh!
It takes _patches_ not _files_. The .sh version specifies "patch" in the
help usage. Okay, I will get this all passing cleanly.

> > +/* Wrap indirect calls with KCFI type for anti-merging.  */
> > +static unsigned int
> > +kcfi_instrument (void)
> > +{
> > +  /* Process current function for call instrumentation only.
> > +     Type ID setting is handled by the separate IPA pass.  */
> > +
> > +  basic_block bb;
> > +
> > +  FOR_EACH_BB_FN (bb, cfun)
> > +    {
> > +      gimple_stmt_iterator gsi;
> > +      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
> > + {
> > +  gimple *stmt = gsi_stmt (gsi);
> > +
> > +  if (!is_gimple_call (stmt))
> > +    continue;
> > +
> > +  gcall *call_stmt = as_a <gcall *> (stmt);
> > +
> > +  // Skip internal calls - we only instrument indirect calls
> > +  if (gimple_call_internal_p (call_stmt))
> > +    continue;
> > +
> > +  tree fndecl = gimple_call_fndecl (call_stmt);
> > +
> > +  // Only process indirect calls (no fndecl)
> > +  if (fndecl)
> > +    continue;
> > +
> > +  tree fn = gimple_call_fn (call_stmt);
> > +  if (!is_kcfi_indirect_call (fn))
> > +    continue;
> > +
> > +  // Get the function type to compute KCFI type ID
> > +  tree fn_type = gimple_call_fntype (call_stmt);
> > +  gcc_assert (fn_type);
> > +  if (TREE_CODE (fn_type) != FUNCTION_TYPE)
> > +    continue;
> > +
> > +  uint32_t type_id = compute_kcfi_type_id (fn_type);
> > +
> > +  // Create KCFI wrapper type for this call
> > +  tree wrapper_type = create_kcfi_wrapper_type (fn_type, type_id);
> Again, the new “type_id” has been attached as an attribute of “fn_type” here, 

The attribute is attached during IPA. This is run before that, but as I
mentioned, this is the call-site handling, and the IPA pass is for
globally associating a type-id to the function for all other uses
(preambles, weak symbols, etc).

> > +  // Create a temporary variable for the wrapped function pointer
> > +  tree wrapper_ptr_type = build_pointer_type (wrapper_type);
> > +  tree wrapper_tmp = create_tmp_var (wrapper_ptr_type, "kcfi_wrapper");
> > +
> > +  // Create assignment: wrapper_tmp = (wrapper_ptr_type) fn
> > +  tree cast_expr = build1 (NOP_EXPR, wrapper_ptr_type, fn);
> > +  gimple *cast_stmt = gimple_build_assign (wrapper_tmp, cast_expr);
> > +  gsi_insert_before (&gsi, cast_stmt, GSI_SAME_STMT);
> > +
> 
> Why the additional wrapper_ptr_type, wrapper_tmp and new assignment stmt
> Are needed here?

Based on my understanding of the requirements for GIMPLE here is that I
needed a cast between the original and the wrapper, and an assignment
for SSA. It's converting the call like:

  Original: result = fn(arg1, arg2)
  Becomes:  result = wrapper(arg1, arg2, type_id)

I'm open to whatever alternative is needed here. I tried to capture the
merging issue with gcc/testsuite/gcc.dg/kcfi/kcfi-call-sharing.c

My other test methodology is "does Linux boot?" ;)

-- 
Kees Cook

Reply via email to