diff --git a/kernel/.clang-format b/kernel/.clang-format index 48405c54..10dc5a9a 100644 --- a/kernel/.clang-format +++ b/kernel/.clang-format @@ -1,10 +1,10 @@ # SPDX-License-Identifier: GPL-2.0 # -# clang-format configuration file. Intended for clang-format >= 11. +# clang-format configuration file. Intended for clang-format >= 4. # # For more information, see: # -# Documentation/dev-tools/clang-format.rst +# Documentation/process/clang-format.rst # https://clang.llvm.org/docs/ClangFormat.html # https://clang.llvm.org/docs/ClangFormatStyleOptions.html # @@ -13,7 +13,7 @@ AccessModifierOffset: -4 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false -AlignEscapedNewlines: Left +#AlignEscapedNewlines: Left # Unknown to clang-format-4.0 AlignOperands: true AlignTrailingComments: false AllowAllParametersOfDeclarationOnNextLine: false @@ -37,24 +37,24 @@ BraceWrapping: AfterObjCDeclaration: false AfterStruct: false AfterUnion: false - AfterExternBlock: false + #AfterExternBlock: false # Unknown to clang-format-5.0 BeforeCatch: false BeforeElse: false IndentBraces: false - SplitEmptyFunction: true - SplitEmptyRecord: true - SplitEmptyNamespace: true + #SplitEmptyFunction: true # Unknown to clang-format-4.0 + #SplitEmptyRecord: true # Unknown to clang-format-4.0 + #SplitEmptyNamespace: true # Unknown to clang-format-4.0 BreakBeforeBinaryOperators: None BreakBeforeBraces: Custom -BreakBeforeInheritanceComma: false +#BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0 BreakBeforeTernaryOperators: false BreakConstructorInitializersBeforeComma: false -BreakConstructorInitializers: BeforeComma +#BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0 BreakAfterJavaFieldAnnotations: false BreakStringLiterals: false ColumnLimit: 80 CommentPragmas: '^ IWYU pragma:' -CompactNamespaces: false +#CompactNamespaces: false # Unknown to clang-format-4.0 ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 8 ContinuationIndentWidth: 8 @@ -62,61 +62,39 @@ Cpp11BracedListStyle: false DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false -FixNamespaceComments: false +#FixNamespaceComments: false # Unknown to clang-format-4.0 # Taken from: -# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ tools/ \ +# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \ # | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \ -# | LC_ALL=C sort -u +# | sort | uniq ForEachMacros: - - '__ata_qc_for_each' - - '__bio_for_each_bvec' - - '__bio_for_each_segment' - - '__evlist__for_each_entry' - - '__evlist__for_each_entry_continue' - - '__evlist__for_each_entry_from' - - '__evlist__for_each_entry_reverse' - - '__evlist__for_each_entry_safe' - - '__for_each_mem_range' - - '__for_each_mem_range_rev' - - '__for_each_thread' - - '__hlist_for_each_rcu' - - '__map__for_each_symbol_by_name' - - '__pci_bus_for_each_res0' - - '__pci_bus_for_each_res1' - - '__pci_dev_for_each_res0' - - '__pci_dev_for_each_res1' - - '__perf_evlist__for_each_entry' - - '__perf_evlist__for_each_entry_reverse' - - '__perf_evlist__for_each_entry_safe' - - '__rq_for_each_bio' - - '__shost_for_each_device' - - '__sym_for_each' - - '_for_each_counter' - 'apei_estatus_for_each_section' - 'ata_for_each_dev' - 'ata_for_each_link' + - '__ata_qc_for_each' - 'ata_qc_for_each' - 'ata_qc_for_each_raw' - 'ata_qc_for_each_with_internal' - 'ax25_for_each' - 'ax25_uid_for_each' + - '__bio_for_each_bvec' - 'bio_for_each_bvec' - 'bio_for_each_bvec_all' - - 'bio_for_each_folio_all' - 'bio_for_each_integrity_vec' + - '__bio_for_each_segment' - 'bio_for_each_segment' - 'bio_for_each_segment_all' - 'bio_list_for_each' - 'bip_for_each_vec' + - 'bitmap_for_each_clear_region' + - 'bitmap_for_each_set_region' + - 'blkg_for_each_descendant_post' + - 'blkg_for_each_descendant_pre' + - 'blk_queue_for_each_rl' - 'bond_for_each_slave' - 'bond_for_each_slave_rcu' - - 'bpf_for_each' - - 'bpf_for_each_reg_in_vstate' - - 'bpf_for_each_reg_in_vstate_mask' - 'bpf_for_each_spilled_reg' - - 'bpf_object__for_each_map' - - 'bpf_object__for_each_program' - 'btree_for_each_safe128' - 'btree_for_each_safe32' - 'btree_for_each_safe64' @@ -124,8 +102,6 @@ ForEachMacros: - 'card_for_each_dev' - 'cgroup_taskset_for_each' - 'cgroup_taskset_for_each_leader' - - 'cpu_aggr_map__for_each_idx' - - 'cpufreq_for_each_efficient_entry_idx' - 'cpufreq_for_each_entry' - 'cpufreq_for_each_entry_idx' - 'cpufreq_for_each_valid_entry' @@ -133,30 +109,8 @@ ForEachMacros: - 'css_for_each_child' - 'css_for_each_descendant_post' - 'css_for_each_descendant_pre' - - 'damon_for_each_region' - - 'damon_for_each_region_from' - - 'damon_for_each_region_safe' - - 'damon_for_each_scheme' - - 'damon_for_each_scheme_safe' - - 'damon_for_each_target' - - 'damon_for_each_target_safe' - - 'damos_for_each_filter' - - 'damos_for_each_filter_safe' - - 'damos_for_each_ops_filter' - - 'damos_for_each_ops_filter_safe' - - 'damos_for_each_quota_goal' - - 'damos_for_each_quota_goal_safe' - - 'data__for_each_file' - - 'data__for_each_file_new' - - 'data__for_each_file_start' - - 'def_for_each_cpu' - 'device_for_each_child_node' - - 'device_for_each_child_node_scoped' - - 'dma_fence_array_for_each' - 'dma_fence_chain_for_each' - - 'dma_fence_unwrap_for_each' - - 'dma_resv_for_each_fence' - - 'dma_resv_for_each_fence_unlocked' - 'do_for_each_ftrace_op' - 'drm_atomic_crtc_for_each_plane' - 'drm_atomic_crtc_state_for_each_plane' @@ -165,12 +119,9 @@ ForEachMacros: - 'drm_client_for_each_connector_iter' - 'drm_client_for_each_modeset' - 'drm_connector_for_each_possible_encoder' - - 'drm_exec_for_each_locked_object' - - 'drm_exec_for_each_locked_object_reverse' - 'drm_for_each_bridge_in_chain' - 'drm_for_each_connector_iter' - 'drm_for_each_crtc' - - 'drm_for_each_crtc_reverse' - 'drm_for_each_encoder' - 'drm_for_each_encoder_mask' - 'drm_for_each_fb' @@ -178,64 +129,19 @@ ForEachMacros: - 'drm_for_each_plane' - 'drm_for_each_plane_mask' - 'drm_for_each_privobj' - - 'drm_gem_for_each_gpuvm_bo' - - 'drm_gem_for_each_gpuvm_bo_safe' - - 'drm_gpusvm_for_each_range' - - 'drm_gpuva_for_each_op' - - 'drm_gpuva_for_each_op_from_reverse' - - 'drm_gpuva_for_each_op_reverse' - - 'drm_gpuva_for_each_op_safe' - - 'drm_gpuvm_bo_for_each_va' - - 'drm_gpuvm_bo_for_each_va_safe' - - 'drm_gpuvm_for_each_va' - - 'drm_gpuvm_for_each_va_range' - - 'drm_gpuvm_for_each_va_range_safe' - - 'drm_gpuvm_for_each_va_safe' - 'drm_mm_for_each_hole' - 'drm_mm_for_each_node' - 'drm_mm_for_each_node_in_range' - 'drm_mm_for_each_node_safe' - - 'dsa_switch_for_each_available_port' - - 'dsa_switch_for_each_cpu_port' - - 'dsa_switch_for_each_cpu_port_continue_reverse' - - 'dsa_switch_for_each_port' - - 'dsa_switch_for_each_port_continue_reverse' - - 'dsa_switch_for_each_port_safe' - - 'dsa_switch_for_each_user_port' - - 'dsa_switch_for_each_user_port_continue_reverse' - - 'dsa_tree_for_each_cpu_port' - - 'dsa_tree_for_each_user_port' - - 'dsa_tree_for_each_user_port_continue_reverse' - - 'dso__for_each_symbol' - - 'elf_hash_for_each_possible' - - 'elf_symtab__for_each_symbol' - - 'evlist__for_each_cpu' - - 'evlist__for_each_entry' - - 'evlist__for_each_entry_continue' - - 'evlist__for_each_entry_from' - - 'evlist__for_each_entry_reverse' - - 'evlist__for_each_entry_safe' - 'flow_action_for_each' - - 'for_each_acpi_consumer_dev' - - 'for_each_acpi_dev_match' - 'for_each_active_dev_scope' - 'for_each_active_drhd_unit' - 'for_each_active_iommu' - - 'for_each_active_irq' - - 'for_each_active_route' - 'for_each_aggr_pgid' - - 'for_each_alloc_capable_rdt_resource' - - 'for_each_and_bit' - - 'for_each_andnot_bit' - 'for_each_available_child_of_node' - - 'for_each_available_child_of_node_scoped' - - 'for_each_bench' - 'for_each_bio' - 'for_each_board_func_rsrc' - - 'for_each_btf_ext_rec' - - 'for_each_btf_ext_sec' - 'for_each_bvec' - - 'for_each_capable_rdt_resource' - 'for_each_card_auxs' - 'for_each_card_auxs_safe' - 'for_each_card_components' @@ -248,32 +154,22 @@ ForEachMacros: - 'for_each_card_widgets_safe' - 'for_each_cgroup_storage_type' - 'for_each_child_of_node' - - 'for_each_child_of_node_scoped' - - 'for_each_child_of_node_with_prefix' - 'for_each_clear_bit' - 'for_each_clear_bit_from' - - 'for_each_clear_bitrange' - - 'for_each_clear_bitrange_from' - - 'for_each_cmd' - 'for_each_cmsghdr' - - 'for_each_collection' - - 'for_each_comp_order' - 'for_each_compatible_node' - 'for_each_component_dais' - 'for_each_component_dais_safe' - - 'for_each_conduit' + - 'for_each_comp_order' - 'for_each_console' - - 'for_each_console_srcu' - 'for_each_cpu' - 'for_each_cpu_and' - - 'for_each_cpu_andnot' - - 'for_each_cpu_from' - - 'for_each_cpu_or' + - 'for_each_cpu_not' - 'for_each_cpu_wrap' - 'for_each_dapm_widgets' - - 'for_each_dedup_cand' - 'for_each_dev_addr' - 'for_each_dev_scope' + - 'for_each_displayid_db' - 'for_each_dma_cap_mask' - 'for_each_dpcm_be' - 'for_each_dpcm_be_rollback' @@ -286,160 +182,107 @@ ForEachMacros: - 'for_each_element' - 'for_each_element_extid' - 'for_each_element_id' - - 'for_each_enabled_cpu' - 'for_each_endpoint_of_node' - - 'for_each_event' - - 'for_each_event_tps' - 'for_each_evictable_lru' - 'for_each_fib6_node_rt_rcu' - 'for_each_fib6_walker_rt' - - 'for_each_file_lock' + - 'for_each_free_mem_pfn_range_in_zone' - 'for_each_free_mem_pfn_range_in_zone_from' - 'for_each_free_mem_range' - 'for_each_free_mem_range_reverse' - 'for_each_func_rsrc' - - 'for_each_gpiochip_node' - - 'for_each_group_evsel' - - 'for_each_group_evsel_head' - - 'for_each_group_member' - - 'for_each_group_member_head' - 'for_each_hstate' - - 'for_each_hwgpio' - - 'for_each_hwgpio_in_range' - 'for_each_if' - - 'for_each_inject_fn' - - 'for_each_insn' - - 'for_each_insn_op_loc' - - 'for_each_insn_prefix' - - 'for_each_intid' - 'for_each_iommu' - 'for_each_ip_tunnel_rcu' - - 'for_each_irq_desc' - 'for_each_irq_nr' - - 'for_each_lang' - - 'for_each_link_ch_maps' - 'for_each_link_codecs' - 'for_each_link_cpus' - 'for_each_link_platforms' - 'for_each_lru' - 'for_each_matching_node' - 'for_each_matching_node_and_match' - - 'for_each_media_entity_data_link' - - 'for_each_mem_pfn_range' - - 'for_each_mem_range' - - 'for_each_mem_range_rev' - - 'for_each_mem_region' - 'for_each_member' - - 'for_each_memory' + - 'for_each_mem_region' + - 'for_each_memblock_type' + - 'for_each_memcg_cache_index' + - 'for_each_mem_pfn_range' + - '__for_each_mem_range' + - 'for_each_mem_range' + - '__for_each_mem_range_rev' + - 'for_each_mem_range_rev' - 'for_each_migratetype_order' - - 'for_each_missing_reg' - - 'for_each_mle_subelement' - - 'for_each_mod_mem_type' - - 'for_each_mon_capable_rdt_resource' - - 'for_each_mp_bvec' + - 'for_each_msi_entry' + - 'for_each_msi_entry_safe' - 'for_each_net' - 'for_each_net_continue_reverse' - - 'for_each_net_rcu' - 'for_each_netdev' - 'for_each_netdev_continue' - 'for_each_netdev_continue_rcu' - 'for_each_netdev_continue_reverse' - - 'for_each_netdev_dump' - 'for_each_netdev_feature' - 'for_each_netdev_in_bond_rcu' - 'for_each_netdev_rcu' - 'for_each_netdev_reverse' - 'for_each_netdev_safe' + - 'for_each_net_rcu' - 'for_each_new_connector_in_state' - 'for_each_new_crtc_in_state' - 'for_each_new_mst_mgr_in_state' - 'for_each_new_plane_in_state' - - 'for_each_new_plane_in_state_reverse' - 'for_each_new_private_obj_in_state' - - 'for_each_new_reg' - - 'for_each_nhlt_endpoint' - - 'for_each_nhlt_endpoint_fmtcfg' - - 'for_each_nhlt_fmtcfg' - 'for_each_node' - 'for_each_node_by_name' - 'for_each_node_by_type' - 'for_each_node_mask' - - 'for_each_node_numadist' - 'for_each_node_state' - 'for_each_node_with_cpus' - 'for_each_node_with_property' - 'for_each_nonreserved_multicast_dest_pgid' - - 'for_each_numa_hop_mask' - 'for_each_of_allnodes' - 'for_each_of_allnodes_from' - 'for_each_of_cpu_node' - - 'for_each_of_graph_port' - - 'for_each_of_graph_port_endpoint' - 'for_each_of_pci_range' - 'for_each_old_connector_in_state' - 'for_each_old_crtc_in_state' - 'for_each_old_mst_mgr_in_state' - - 'for_each_old_plane_in_state' - - 'for_each_old_private_obj_in_state' - 'for_each_oldnew_connector_in_state' - 'for_each_oldnew_crtc_in_state' - 'for_each_oldnew_mst_mgr_in_state' - 'for_each_oldnew_plane_in_state' - 'for_each_oldnew_plane_in_state_reverse' - 'for_each_oldnew_private_obj_in_state' + - 'for_each_old_plane_in_state' + - 'for_each_old_private_obj_in_state' - 'for_each_online_cpu' - - 'for_each_online_cpu_wrap' - 'for_each_online_node' - 'for_each_online_pgdat' - - 'for_each_or_bit' - - 'for_each_page_ext' - - 'for_each_path' - 'for_each_pci_bridge' - 'for_each_pci_dev' + - 'for_each_pci_msi_entry' - 'for_each_pcm_streams' - 'for_each_physmem_range' - 'for_each_populated_zone' - 'for_each_possible_cpu' - - 'for_each_possible_cpu_wrap' - - 'for_each_present_blessed_reg' - 'for_each_present_cpu' - - 'for_each_present_section_nr' - 'for_each_prime_number' - 'for_each_prime_number_from' - - 'for_each_probe_cache_entry' - 'for_each_process' - 'for_each_process_thread' - - 'for_each_prop_codec_conf' - - 'for_each_prop_dai_codec' - - 'for_each_prop_dai_cpu' - - 'for_each_prop_dlc_codecs' - - 'for_each_prop_dlc_cpus' - - 'for_each_prop_dlc_platforms' - 'for_each_property_of_node' - - 'for_each_rdt_resource' - - 'for_each_reg' - - 'for_each_reg_filtered' - - 'for_each_reloc' - - 'for_each_reloc_from' + - 'for_each_registered_fb' - 'for_each_requested_gpio' - 'for_each_requested_gpio_in_range' - - 'for_each_reserved_child_of_node' - 'for_each_reserved_mem_range' - 'for_each_reserved_mem_region' - - 'for_each_rtd_ch_maps' - 'for_each_rtd_codec_dais' + - 'for_each_rtd_codec_dais_rollback' - 'for_each_rtd_components' - 'for_each_rtd_cpu_dais' + - 'for_each_rtd_cpu_dais_rollback' - 'for_each_rtd_dais' - - 'for_each_rtd_dais_reverse' - - 'for_each_sband_iftype_data' - - 'for_each_script' - - 'for_each_sec' - 'for_each_set_bit' - 'for_each_set_bit_from' - - 'for_each_set_bit_wrap' - - 'for_each_set_bitrange' - - 'for_each_set_bitrange_from' - 'for_each_set_clump8' - 'for_each_sg' - 'for_each_sg_dma_page' @@ -449,37 +292,22 @@ ForEachMacros: - 'for_each_sgtable_page' - 'for_each_sgtable_sg' - 'for_each_sibling_event' - - 'for_each_sta_active_link' - 'for_each_subelement' - 'for_each_subelement_extid' - 'for_each_subelement_id' - - 'for_each_sublist' - - 'for_each_subsystem' - - 'for_each_suite' - - 'for_each_supported_activate_fn' - - 'for_each_supported_inject_fn' - - 'for_each_sym' + - '__for_each_thread' - 'for_each_thread' - - 'for_each_token' - 'for_each_unicast_dest_pgid' - - 'for_each_valid_link' - - 'for_each_vif_active_link' - - 'for_each_vma' - - 'for_each_vma_range' - - 'for_each_vsi' - 'for_each_wakeup_source' - 'for_each_zone' - 'for_each_zone_zonelist' - 'for_each_zone_zonelist_nodemask' - - 'func_for_each_insn' - 'fwnode_for_each_available_child_node' - 'fwnode_for_each_child_node' - - 'fwnode_for_each_parent_node' - 'fwnode_graph_for_each_endpoint' - 'gadget_for_each_ep' - 'genradix_for_each' - 'genradix_for_each_from' - - 'genradix_for_each_reverse' - 'hash_for_each' - 'hash_for_each_possible' - 'hash_for_each_possible_rcu' @@ -487,13 +315,7 @@ ForEachMacros: - 'hash_for_each_possible_safe' - 'hash_for_each_rcu' - 'hash_for_each_safe' - - 'hashmap__for_each_entry' - - 'hashmap__for_each_entry_safe' - - 'hashmap__for_each_key_entry' - - 'hashmap__for_each_key_entry_safe' - 'hctx_for_each_ctx' - - 'hists__for_each_format' - - 'hists__for_each_sort_list' - 'hlist_bl_for_each_entry' - 'hlist_bl_for_each_entry_rcu' - 'hlist_bl_for_each_entry_safe' @@ -508,7 +330,7 @@ ForEachMacros: - 'hlist_for_each_entry_rcu_bh' - 'hlist_for_each_entry_rcu_notrace' - 'hlist_for_each_entry_safe' - - 'hlist_for_each_entry_srcu' + - '__hlist_for_each_rcu' - 'hlist_for_each_safe' - 'hlist_nulls_for_each_entry' - 'hlist_nulls_for_each_entry_from' @@ -516,19 +338,17 @@ ForEachMacros: - 'hlist_nulls_for_each_entry_safe' - 'i3c_bus_for_each_i2cdev' - 'i3c_bus_for_each_i3cdev' + - 'ide_host_for_each_port' + - 'ide_port_for_each_dev' + - 'ide_port_for_each_present_dev' - 'idr_for_each_entry' - 'idr_for_each_entry_continue' - 'idr_for_each_entry_continue_ul' - 'idr_for_each_entry_ul' - - 'iio_for_each_active_channel' - 'in_dev_for_each_ifa_rcu' - 'in_dev_for_each_ifa_rtnl' - - 'in_dev_for_each_ifa_rtnl_net' - 'inet_bind_bucket_for_each' - - 'interval_tree_for_each_span' - - 'intlist__for_each_entry' - - 'intlist__for_each_entry_safe' - - 'kcore_copy__for_each_phdr' + - 'inet_lhash2_for_each_icsk_rcu' - 'key_for_each' - 'key_for_each_safe' - 'klp_for_each_func' @@ -539,9 +359,7 @@ ForEachMacros: - 'klp_for_each_object_static' - 'kunit_suite_for_each_test_case' - 'kvm_for_each_memslot' - - 'kvm_for_each_memslot_in_gfn_range' - 'kvm_for_each_vcpu' - - 'libbpf_nla_for_each_attr' - 'list_for_each' - 'list_for_each_codec' - 'list_for_each_codec_safe' @@ -560,53 +378,29 @@ ForEachMacros: - 'list_for_each_entry_safe_continue' - 'list_for_each_entry_safe_from' - 'list_for_each_entry_safe_reverse' - - 'list_for_each_entry_srcu' - - 'list_for_each_from' - 'list_for_each_prev' - 'list_for_each_prev_safe' - - 'list_for_each_rcu' - 'list_for_each_safe' - 'llist_for_each' - 'llist_for_each_entry' - 'llist_for_each_entry_safe' - 'llist_for_each_safe' - - 'lwq_for_each_safe' - - 'map__for_each_symbol' - - 'map__for_each_symbol_by_name' - - 'mas_for_each' - - 'mas_for_each_rev' - 'mci_for_each_dimm' - 'media_device_for_each_entity' - 'media_device_for_each_intf' - 'media_device_for_each_link' - 'media_device_for_each_pad' - - 'media_entity_for_each_pad' - - 'media_pipeline_for_each_entity' - - 'media_pipeline_for_each_pad' - - 'mlx5_lag_for_each_peer_mdev' - - 'mptcp_for_each_subflow' - - 'msi_domain_for_each_desc' - - 'msi_for_each_desc' - - 'mt_for_each' - - 'nanddev_io_for_each_block' - 'nanddev_io_for_each_page' - - 'neigh_for_each_in_bucket' - - 'neigh_for_each_in_bucket_rcu' - - 'neigh_for_each_in_bucket_safe' - 'netdev_for_each_lower_dev' - 'netdev_for_each_lower_private' - 'netdev_for_each_lower_private_rcu' - 'netdev_for_each_mc_addr' - - 'netdev_for_each_synced_mc_addr' - - 'netdev_for_each_synced_uc_addr' - 'netdev_for_each_uc_addr' - 'netdev_for_each_upper_dev_rcu' - 'netdev_hw_addr_list_for_each' - 'nft_rule_for_each_expr' - 'nla_for_each_attr' - - 'nla_for_each_attr_type' - 'nla_for_each_nested' - - 'nla_for_each_nested_type' - 'nlmsg_for_each_attr' - 'nlmsg_for_each_msg' - 'nr_neigh_for_each' @@ -617,26 +411,8 @@ ForEachMacros: - 'of_property_for_each_string' - 'of_property_for_each_u32' - 'pci_bus_for_each_resource' - - 'pci_dev_for_each_resource' - - 'pcl_for_each_chunk' - - 'pcl_for_each_segment' - 'pcm_for_each_format' - - 'perf_config_items__for_each_entry' - - 'perf_config_sections__for_each_entry' - - 'perf_config_set__for_each_entry' - - 'perf_cpu_map__for_each_cpu' - - 'perf_cpu_map__for_each_cpu_skip_any' - - 'perf_cpu_map__for_each_idx' - - 'perf_evlist__for_each_entry' - - 'perf_evlist__for_each_entry_reverse' - - 'perf_evlist__for_each_entry_safe' - - 'perf_evlist__for_each_evsel' - - 'perf_evlist__for_each_mmap' - - 'perf_evsel_for_each_per_thread_period_safe' - - 'perf_hpp_list__for_each_format' - - 'perf_hpp_list__for_each_format_safe' - - 'perf_hpp_list__for_each_sort_list' - - 'perf_hpp_list__for_each_sort_list_safe' + - 'ping_portaddr_for_each_entry' - 'plist_for_each' - 'plist_for_each_continue' - 'plist_for_each_entry' @@ -650,7 +426,6 @@ ForEachMacros: - 'queue_for_each_hw_ctx' - 'radix_tree_for_each_slot' - 'radix_tree_for_each_tagged' - - 'rb_for_each' - 'rbtree_postorder_for_each_entry_safe' - 'rdma_for_each_block' - 'rdma_for_each_port' @@ -668,24 +443,18 @@ ForEachMacros: - 'rht_for_each_from' - 'rht_for_each_rcu' - 'rht_for_each_rcu_from' + - '__rq_for_each_bio' - 'rq_for_each_bvec' - 'rq_for_each_segment' - - 'rq_list_for_each' - - 'rq_list_for_each_safe' - - 'sample_read_group__for_each' - 'scsi_for_each_prot_sg' - 'scsi_for_each_sg' - 'sctp_for_each_hentry' - 'sctp_skb_for_each' - - 'sec_for_each_insn' - - 'sec_for_each_insn_continue' - - 'sec_for_each_insn_from' - - 'sec_for_each_sym' - 'shdma_for_each_chan' + - '__shost_for_each_device' - 'shost_for_each_device' - 'sk_for_each' - 'sk_for_each_bound' - - 'sk_for_each_bound_safe' - 'sk_for_each_entry_offset_rcu' - 'sk_for_each_from' - 'sk_for_each_rcu' @@ -699,20 +468,8 @@ ForEachMacros: - 'snd_soc_dapm_widget_for_each_path_safe' - 'snd_soc_dapm_widget_for_each_sink_path' - 'snd_soc_dapm_widget_for_each_source_path' - - 'sparsebit_for_each_set_range' - - 'strlist__for_each_entry' - - 'strlist__for_each_entry_safe' - - 'sym_for_each_insn' - - 'sym_for_each_insn_continue_reverse' - - 'symbols__for_each_entry' - 'tb_property_for_each' - - 'tcf_act_for_each_action' - 'tcf_exts_for_each_action' - - 'test_suite__for_each_test_case' - - 'tool_pmu__for_each_event' - - 'ttm_bo_lru_for_each_reserved_guarded' - - 'ttm_resource_manager_for_each_res' - - 'udp_lrpa_for_each_entry_rcu' - 'udp_portaddr_for_each_entry' - 'udp_portaddr_for_each_entry_rcu' - 'usb_hub_for_each_child' @@ -722,15 +479,7 @@ ForEachMacros: - 'v4l2_m2m_for_each_src_buf' - 'v4l2_m2m_for_each_src_buf_safe' - 'virtio_device_for_each_vq' - - 'vkms_config_for_each_connector' - - 'vkms_config_for_each_crtc' - - 'vkms_config_for_each_encoder' - - 'vkms_config_for_each_plane' - - 'vkms_config_connector_for_each_possible_encoder' - - 'vkms_config_encoder_for_each_possible_crtc' - - 'vkms_config_plane_for_each_possible_crtc' - 'while_for_each_ftrace_op' - - 'workloads__for_each' - 'xa_for_each' - 'xa_for_each_marked' - 'xa_for_each_range' @@ -743,20 +492,15 @@ ForEachMacros: - 'xbc_node_for_each_array_value' - 'xbc_node_for_each_child' - 'xbc_node_for_each_key_value' - - 'xbc_node_for_each_subkey' - - 'ynl_attr_for_each' - - 'ynl_attr_for_each_nested' - - 'ynl_attr_for_each_payload' - 'zorro_for_each_dev' -IncludeBlocks: Preserve +#IncludeBlocks: Preserve # Unknown to clang-format-5.0 IncludeCategories: - Regex: '.*' Priority: 1 IncludeIsMainRegex: '(Test)?$' IndentCaseLabels: false -IndentGotoLabels: false -IndentPPDirectives: None +#IndentPPDirectives: None # Unknown to clang-format-5.0 IndentWidth: 8 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave @@ -766,13 +510,13 @@ MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: None -ObjCBinPackProtocolList: Auto +#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0 ObjCBlockIndentWidth: 8 ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: true # Taken from git's rules -PenaltyBreakAssignment: 10 +#PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0 PenaltyBreakBeforeFirstCallParameter: 30 PenaltyBreakComment: 10 PenaltyBreakFirstLessLess: 0 @@ -783,14 +527,14 @@ PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Right ReflowComments: false SortIncludes: false -SortUsingDeclarations: false +#SortUsingDeclarations: false # Unknown to clang-format-4.0 SpaceAfterCStyleCast: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true -SpaceBeforeCtorInitializerColon: true -SpaceBeforeInheritanceColon: true -SpaceBeforeParens: ControlStatementsExceptForEachMacros -SpaceBeforeRangeBasedForLoopColon: true +#SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0 +#SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0 +SpaceBeforeParens: ControlStatements +#SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0 SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false diff --git a/kernel/Kconfig b/kernel/Kconfig index 46ee2d1f..7f0e6ccc 100644 --- a/kernel/Kconfig +++ b/kernel/Kconfig @@ -15,15 +15,6 @@ config KSU_DEBUG help Enable KernelSU debug mode. -config KSU_THRONE_TRACKER_LEGACY - bool "Use legacy throne tracker (packages.list scanning)" - depends on KSU - default n - help - Use legacy throne tracker that scans packages.list for app UIDs. - This is kept for Ultra-Legacy Linux 4.4-3.X kernels which are prone to deadlocks. - Enable this if default scanning deadlocks/crashes on you. - config KSU_ALLOWLIST_WORKAROUND bool "KernelSU Session init keyring workaround" depends on KSU @@ -32,14 +23,6 @@ config KSU_ALLOWLIST_WORKAROUND Enable session keyring init workaround for problematic devices. Useful for situations where the SU allowlist is not kept after a reboot. -config KSU_CMDLINE - bool "Enable KernelSU cmdline" - depends on KSU && KSU != m - default n - help - Enable a cmdline called kernelsu.enabled - Value 1 means enabled, value 0 means disabled. - config KPM bool "Enable SukiSU KPM" depends on KSU && 64BIT diff --git a/kernel/Makefile b/kernel/Makefile index 7336ef1b..1ea3d19a 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -3,7 +3,12 @@ kernelsu-objs += allowlist.o kernelsu-objs += dynamic_manager.o kernelsu-objs += apk_sign.o kernelsu-objs += sucompat.o +kernelsu-objs += throne_tracker.o +kernelsu-objs += pkg_observer.o kernelsu-objs += core_hook.o +kernelsu-objs += supercalls.o +kernelsu-objs += feature.o +kernelsu-objs += throne_tracker.o kernelsu-objs += ksud.o kernelsu-objs += embed_ksud.o kernelsu-objs += kernel_compat.o @@ -13,13 +18,6 @@ ifeq ($(CONFIG_KSU_TRACEPOINT_HOOK), y) kernelsu-objs += ksu_trace.o endif -ifeq ($(CONFIG_KSU_THRONE_TRACKER_LEGACY),y) -$(info -- KernelSU/compat: using legacy throne tracker) -kernelsu-objs += throne_tracker_legacy.o -else -$(info -- KernelSU/compat: using new throne tracker) -kernelsu-objs += throne_tracker.o -endif kernelsu-objs += selinux/selinux.o kernelsu-objs += selinux/sepolicy.o @@ -117,28 +115,20 @@ endif ifeq ($(shell grep "ssize_t kernel_write" $(srctree)/fs/read_write.c | grep -q "const void" ; echo $$?),0) ccflags-y += -DKSU_OPTIONAL_KERNEL_WRITE endif -ifeq ($(shell grep -q "inode_security_struct\s\+\*selinux_inode" $(srctree)/security/selinux/include/objsec.h; echo $$?),0) -ccflags-y += -DKSU_OPTIONAL_SELINUX_INODE -endif ifeq ($(shell grep -q "int\s\+path_umount" $(srctree)/fs/namespace.c; echo $$?),0) ccflags-y += -DKSU_HAS_PATH_UMOUNT endif -# Checks Samsung UH drivers +ifeq ($(shell grep -q "inode_security_struct\s\+\*selinux_inode" $(srctree)/security/selinux/include/objsec.h; echo $$?),0) +ccflags-y += -DKSU_OPTIONAL_SELINUX_INODE +endif + +# Checks Samsung ifeq ($(shell grep -q "CONFIG_KDP_CRED" $(srctree)/kernel/cred.c; echo $$?),0) ccflags-y += -DSAMSUNG_UH_DRIVER_EXIST endif - -# Samsung SELinux Porting ifeq ($(shell grep -q "SEC_SELINUX_PORTING_COMMON" $(srctree)/security/selinux/avc.c; echo $$?),0) ccflags-y += -DSAMSUNG_SELINUX_PORTING endif - -# Check new vfs_getattr() -ifeq ($(shell grep -A1 "^int vfs_getattr" $(srctree)/fs/stat.c | grep -q "query_flags" ; echo $$?),0) -$(info -- KernelSU/compat: new vfs_getattr() found) -ccflags-y += -DKSU_HAS_NEW_VFS_GETATTR -endif - # Function proc_ops check ifeq ($(shell grep -q "struct proc_ops " $(srctree)/include/linux/proc_fs.h; echo $$?),0) ccflags-y += -DKSU_COMPAT_HAS_PROC_OPS @@ -183,6 +173,6 @@ $(info -- KPM is disabled) endif ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat -ccflags-y += -Wno-declaration-after-statement -Wno-unused-function +ccflags-y += -Wno-declaration-after-statement -Wno-unused-function -Wno-missing-prototypes # Keep a new line here!! Because someone may append config diff --git a/kernel/allowlist.c b/kernel/allowlist.c index 16e4eef5..1a1cf9a0 100644 --- a/kernel/allowlist.c +++ b/kernel/allowlist.c @@ -103,7 +103,7 @@ void ksu_show_allow_list(void) struct perm_data *p = NULL; struct list_head *pos = NULL; pr_info("ksu_show_allow_list\n"); - list_for_each(pos, &allow_list) { + list_for_each (pos, &allow_list) { p = list_entry(pos, struct perm_data, list); pr_info("uid :%d, allow: %d\n", p->profile.current_uid, p->profile.allow_su); @@ -131,7 +131,7 @@ bool ksu_get_app_profile(struct app_profile *profile) struct list_head *pos = NULL; bool found = false; - list_for_each(pos, &allow_list) { + list_for_each (pos, &allow_list) { p = list_entry(pos, struct perm_data, list); bool uid_match = profile->current_uid == p->profile.current_uid; if (uid_match) { @@ -188,7 +188,7 @@ bool ksu_set_app_profile(struct app_profile *profile, bool persist) return false; } - list_for_each(pos, &allow_list) { + list_for_each (pos, &allow_list) { p = list_entry(pos, struct perm_data, list); // both uid and package must match, otherwise it will break multiple package with different user id if (profile->current_uid == p->profile.current_uid && @@ -330,7 +330,7 @@ struct root_profile *ksu_get_root_profile(uid_t uid) struct perm_data *p = NULL; struct list_head *pos = NULL; - list_for_each(pos, &allow_list) { + list_for_each (pos, &allow_list) { p = list_entry(pos, struct perm_data, list); if (uid == p->profile.current_uid && p->profile.allow_su) { if (!p->profile.rp_config.use_default) { @@ -348,7 +348,7 @@ bool ksu_get_allow_list(int *array, int *length, bool allow) struct perm_data *p = NULL; struct list_head *pos = NULL; int i = 0; - list_for_each(pos, &allow_list) { + list_for_each (pos, &allow_list) { p = list_entry(pos, struct perm_data, list); // pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow); if (p->profile.allow_su == allow) { @@ -389,7 +389,7 @@ void do_save_allow_list(struct work_struct *work) goto exit; } - list_for_each(pos, &allow_list) { + list_for_each (pos, &allow_list) { p = list_entry(pos, struct perm_data, list); pr_info("save allow list, name: %s uid: %d, allow: %d\n", p->profile.key, p->profile.current_uid, @@ -469,7 +469,7 @@ void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *), bool modified = false; // TODO: use RCU! mutex_lock(&allowlist_mutex); - list_for_each_entry_safe(np, n, &allow_list, list) { + list_for_each_entry_safe (np, n, &allow_list, list) { uid_t uid = np->profile.current_uid; char *package = np->profile.key; // we use this uid for special cases, don't prune it! @@ -532,7 +532,7 @@ void ksu_allowlist_exit(void) // free allowlist mutex_lock(&allowlist_mutex); - list_for_each_entry_safe(np, n, &allow_list, list) { + list_for_each_entry_safe (np, n, &allow_list, list) { list_del(&np->list); kfree(np); } diff --git a/kernel/arch.h b/kernel/arch.h index 326d303f..3222c0a6 100644 --- a/kernel/arch.h +++ b/kernel/arch.h @@ -19,7 +19,7 @@ #define __PT_IP_REG pc #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) -#define PRCTL_SYMBOL "__arm64_sys_prctl" +#define REBOOT_SYMBOL "__arm64_sys_reboot" #define SYS_READ_SYMBOL "__arm64_sys_read" #define SYS_NEWFSTATAT_SYMBOL "__arm64_sys_newfstatat" #define SYS_FSTATAT64_SYMBOL "__arm64_sys_fstatat64" @@ -27,7 +27,7 @@ #define SYS_EXECVE_SYMBOL "__arm64_sys_execve" #define SYS_EXECVE_COMPAT_SYMBOL "__arm64_compat_sys_execve" #else -#define PRCTL_SYMBOL "sys_prctl" +#define REBOOT_SYMBOL "sys_reboot" #define SYS_READ_SYMBOL "sys_read" #define SYS_NEWFSTATAT_SYMBOL "sys_newfstatat" #define SYS_FSTATAT64_SYMBOL "sys_fstatat64" @@ -52,7 +52,7 @@ #define __PT_SP_REG sp #define __PT_IP_REG ip #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) -#define PRCTL_SYMBOL "__x64_sys_prctl" +#define REBOOT_SYMBOL "__x64_sys_reboot" #define SYS_READ_SYMBOL "__x64_sys_read" #define SYS_NEWFSTATAT_SYMBOL "__x64_sys_newfstatat" #define SYS_FSTATAT64_SYMBOL "__x64_sys_fstatat64" @@ -60,7 +60,7 @@ #define SYS_EXECVE_SYMBOL "__x64_sys_execve" #define SYS_EXECVE_COMPAT_SYMBOL "__x64_compat_sys_execve" #else -#define PRCTL_SYMBOL "sys_prctl" +#define PRCTL_SYMBOL "sys_reboot" #define SYS_READ_SYMBOL "sys_read" #define SYS_NEWFSTATAT_SYMBOL "sys_newfstatat" #define SYS_FSTATAT64_SYMBOL "sys_fstatat64" diff --git a/kernel/core_hook.c b/kernel/core_hook.c index c79d5ffa..1206a8f9 100644 --- a/kernel/core_hook.c +++ b/kernel/core_hook.c @@ -1,46 +1,36 @@ +#include +#include +#include +#include #include +#include #include -#include +#include #include #include -#include #include #include -#ifdef CONFIG_KSU_LSM_SECURITY_HOOKS -#include -#endif #include +#include +#include #include #include #include #include -#include #include #include #include #include #include #include -#include -#include - -#include -#include #ifndef KSU_HAS_PATH_UMOUNT #include // sys_umount (<4.17) & ksys_umount (4.17+) #endif -#ifdef MODULE -#include -#include -#include -#include -#include -#endif - #include "allowlist.h" #include "arch.h" #include "core_hook.h" +#include "feature.h" #include "klog.h" // IWYU pragma: keep #include "ksu.h" #include "ksud.h" @@ -49,22 +39,44 @@ #include "throne_tracker.h" #include "throne_comm.h" #include "kernel_compat.h" -#include "dynamic_manager.h" +#include "supercalls.h" -#ifdef CONFIG_KPM -#include "kpm/kpm.h" +bool ksu_module_mounted = false; + +#ifndef DEVPTS_SUPER_MAGIC +#define DEVPTS_SUPER_MAGIC 0x1cd1 #endif -bool ksu_uid_scanner_enabled = false; -static bool ksu_module_mounted = false; +extern int __ksu_handle_devpts(struct inode *inode); // sucompat.c + +#ifdef CONFIG_COMPAT +bool ksu_is_compat __read_mostly = false; +#endif -// selinux/rules.c extern int handle_sepolicy(unsigned long arg3, void __user *arg4); -// sucompat.c -static bool ksu_su_compat_enabled = true; -extern void ksu_sucompat_init(void); -extern void ksu_sucompat_exit(void); +static bool ksu_kernel_umount_enabled = true; + +static int kernel_umount_feature_get(u64 *value) +{ + *value = ksu_kernel_umount_enabled ? 1 : 0; + return 0; +} + +static int kernel_umount_feature_set(u64 value) +{ + bool enable = value != 0; + ksu_kernel_umount_enabled = enable; + pr_info("kernel_umount: set to %d\n", enable); + return 0; +} + +static const struct ksu_feature_handler kernel_umount_handler = { + .feature_id = KSU_FEATURE_KERNEL_UMOUNT, + .name = "kernel_umount", + .get_handler = kernel_umount_feature_get, + .set_handler = kernel_umount_feature_set, +}; static inline bool is_allow_su(void) { @@ -82,10 +94,12 @@ static inline bool is_unsupported_uid(uid_t uid) return appid > LAST_APPLICATION_UID; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION (6, 7, 0) - static struct group_info root_groups = { .usage = REFCOUNT_INIT(2), }; -#else - static struct group_info root_groups = { .usage = ATOMIC_INIT(2) }; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0) +static struct group_info root_groups = { + .usage = REFCOUNT_INIT(2), +}; +#else +static struct group_info root_groups = { .usage = ATOMIC_INIT(2) }; #endif static void setup_groups(struct root_profile *profile, struct cred *cred) @@ -137,7 +151,7 @@ static void disable_seccomp(struct task_struct *tsk) assert_spin_locked(&tsk->sighand->siglock); // disable seccomp -#if defined(CONFIG_GENERIC_ENTRY) && \ +#if defined(CONFIG_GENERIC_ENTRY) && \ LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) clear_syscall_work(SECCOMP); #else @@ -147,12 +161,12 @@ static void disable_seccomp(struct task_struct *tsk) #ifdef CONFIG_SECCOMP tsk->seccomp.mode = 0; if (tsk->seccomp.filter) { - // 5.9+ have filter_count and use seccomp_filter_release + // 5.9+ have filter_count and use seccomp_filter_release #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) seccomp_filter_release(tsk); atomic_set(&tsk->seccomp.filter_count, 0); #else - // for 6.11+ kernel support? + // for 6.11+ kernel support? #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) put_seccomp_filter(tsk); #endif @@ -164,32 +178,32 @@ static void disable_seccomp(struct task_struct *tsk) void escape_to_root(void) { - struct cred *newcreds; + struct cred *cred; - if (current_euid().val == 0) { + cred = prepare_creds(); + if (!cred) { + pr_warn("prepare_creds failed!\n"); + return; + } + + if (cred->euid.val == 0) { pr_warn("Already root, don't escape!\n"); - return; - } - - newcreds = prepare_creds(); - if (newcreds == NULL) { - pr_err("%s: failed to allocate new cred.\n", __func__); + abort_creds(cred); return; } - struct root_profile *profile = - ksu_get_root_profile(newcreds->uid.val); + struct root_profile *profile = ksu_get_root_profile(cred->uid.val); - newcreds->uid.val = profile->uid; - newcreds->suid.val = profile->uid; - newcreds->euid.val = profile->uid; - newcreds->fsuid.val = profile->uid; + cred->uid.val = profile->uid; + cred->suid.val = profile->uid; + cred->euid.val = profile->uid; + cred->fsuid.val = profile->uid; - newcreds->gid.val = profile->gid; - newcreds->fsgid.val = profile->gid; - newcreds->sgid.val = profile->gid; - newcreds->egid.val = profile->gid; - newcreds->securebits = 0; + cred->gid.val = profile->gid; + cred->fsgid.val = profile->gid; + cred->sgid.val = profile->gid; + cred->egid.val = profile->gid; + cred->securebits = 0; BUILD_BUG_ON(sizeof(profile->capabilities.effective) != sizeof(kernel_cap_t)); @@ -197,17 +211,18 @@ void escape_to_root(void) // setup capabilities // we need CAP_DAC_READ_SEARCH becuase `/data/adb/ksud` is not accessible for non root process // we add it here but don't add it to cap_inhertiable, it would be dropped automaticly after exec! - u64 cap_for_ksud = profile->capabilities.effective | - CAP_DAC_READ_SEARCH; - memcpy(&newcreds->cap_effective, &cap_for_ksud, - sizeof(newcreds->cap_effective)); - memcpy(&newcreds->cap_permitted, &profile->capabilities.effective, - sizeof(newcreds->cap_permitted)); - memcpy(&newcreds->cap_bset, &profile->capabilities.effective, - sizeof(newcreds->cap_bset)); + u64 cap_for_ksud = + profile->capabilities.effective | CAP_DAC_READ_SEARCH; + memcpy(&cred->cap_effective, &cap_for_ksud, + sizeof(cred->cap_effective)); + memcpy(&cred->cap_permitted, &profile->capabilities.effective, + sizeof(cred->cap_permitted)); + memcpy(&cred->cap_bset, &profile->capabilities.effective, + sizeof(cred->cap_bset)); - setup_groups(profile, newcreds); - commit_creds(newcreds); + setup_groups(profile, cred); + + commit_creds(cred); spin_lock_irq(¤t->sighand->siglock); disable_seccomp(current); @@ -216,529 +231,27 @@ void escape_to_root(void) setup_selinux(profile->selinux_domain); } -int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry) +void nuke_ext4_sysfs(void) { - if (!current->mm) { - // skip kernel threads - return 0; - } - - if (current_uid().val != 1000) { - // skip non system uid - return 0; - } - - if (!old_dentry || !new_dentry) { - return 0; - } - - // /data/system/packages.list.tmp -> /data/system/packages.list - if (strcmp(new_dentry->d_iname, "packages.list")) { - return 0; - } - - char path[128]; - char *buf = dentry_path_raw(new_dentry, path, sizeof(path)); - if (IS_ERR(buf)) { - pr_err("dentry_path_raw failed.\n"); - return 0; - } - - if (!strstr(buf, "/system/packages.list")) { - return 0; - } - pr_info("renameat: %s -> %s, new path: %s\n", old_dentry->d_iname, - new_dentry->d_iname, buf); - - if (ksu_uid_scanner_enabled) { - ksu_request_userspace_scan(); - } - - track_throne(); - - return 0; -} - #ifdef CONFIG_EXT4_FS -static void nuke_ext4_sysfs(void) -{ struct path path; int err = kern_path("/data/adb/modules", 0, &path); if (err) { - pr_err("nuke path err: %d\n", err); + pr_err("%s: failed to get path, err %d\n", __func__, err); return; } struct super_block *sb = path.dentry->d_inode->i_sb; const char *name = sb->s_type->name; if (strcmp(name, "ext4") != 0) { - pr_info("nuke but module aren't mounted\n"); + pr_info("skipping s_type: %s\n", name); path_put(&path); return; } ext4_unregister_sysfs(sb); path_put(&path); -} -#else -static inline void nuke_ext4_sysfs(void) -{ -} #endif - -static void init_uid_scanner(void) -{ - ksu_uid_init(); - do_load_throne_state(NULL); - - if (ksu_uid_scanner_enabled) { - int ret = ksu_throne_comm_init(); - if (ret != 0) { - pr_err("Failed to initialize throne communication: %d\n", ret); - } - } -} - -static inline bool is_prctl_valid(int option) -{ - // do uid checks first before compare to 0xdeadbeef - if (is_allow_su()) { - if (option == KERNEL_SU_OPTION) - return true; - } - return false; -} - -int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3, - unsigned long arg4, unsigned long arg5) -{ - // if success, we modify the arg5 as result! - u32 *result = (u32 *)arg5; - u32 reply_ok = KERNEL_SU_OPTION; - uid_t current_uid_val = current_uid().val; - - // skip this private space support if uid below 100k - if (current_uid_val < 100000) - goto skip_check; - - uid_t manager_uid = ksu_get_manager_uid(); - if (current_uid_val != manager_uid && - current_uid_val % 100000 == manager_uid) { - ksu_set_manager_uid(current_uid_val); - } - -skip_check: - // yes this causes delay, but this keeps the delay consistent, which is what we want - // with a barrier for safety as the compiler might try to do something smart. - kcompat_barrier(); - if (!is_allow_su()) - return 0; - - // we move it after uid check here so they cannot - // compare 0xdeadbeef call to a non-0xdeadbeef call - if (KERNEL_SU_OPTION != option) - return 0; - - // just continue old logic - bool from_root = 0 == current_uid().val; - bool from_manager = is_manager(); - - if (!from_root && !from_manager) { - // only root or manager can access this interface - return 0; - } - -#ifdef CONFIG_KSU_DEBUG - pr_info("option: 0x%x, cmd: %ld\n", option, arg2); -#endif - - if (arg2 == CMD_BECOME_MANAGER) { - if (from_manager) { - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("become_manager: prctl reply error\n"); - } - return 0; - } - return 0; - } - - if (arg2 == CMD_GRANT_ROOT) { - if (is_allow_su()) { - pr_info("allow root for: %d\n", current_uid().val); - escape_to_root(); - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("grant_root: prctl reply error\n"); - } - } - return 0; - } - - // Both root manager and root processes should be allowed to get version - if (arg2 == CMD_GET_VERSION) { - u32 version = KERNEL_SU_VERSION; - if (copy_to_user(arg3, &version, sizeof(version))) { - pr_err("prctl reply error, cmd: %lu\n", arg2); - } - u32 version_flags = 2; -#ifdef MODULE - version_flags |= 0x1; -#endif - if (arg4 && - copy_to_user(arg4, &version_flags, sizeof(version_flags))) { - pr_err("prctl reply error, cmd: %lu\n", arg2); - } - return 0; - } - - // Allow root manager to get full version strings - if (arg2 == CMD_GET_FULL_VERSION) { - char ksu_version_full[KSU_FULL_VERSION_STRING] = { 0 }; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) - strscpy(ksu_version_full, KSU_VERSION_FULL, - KSU_FULL_VERSION_STRING); -#else - strlcpy(ksu_version_full, KSU_VERSION_FULL, - KSU_FULL_VERSION_STRING); -#endif - if (copy_to_user((void __user *)arg3, ksu_version_full, - KSU_FULL_VERSION_STRING)) { - pr_err("prctl reply error, cmd: %lu\n", arg2); - return -EFAULT; - } - return 0; - } - - // Allow the root manager to configure dynamic manageratures - if (arg2 == CMD_DYNAMIC_MANAGER) { - if (!from_root && !from_manager) { - return 0; - } - - struct dynamic_manager_user_config config; - - if (copy_from_user(&config, (void __user *)arg3, - sizeof(config))) { - pr_err("copy dynamic manager config failed\n"); - return 0; - } - - int ret = ksu_handle_dynamic_manager(&config); - - if (ret == 0 && config.operation == DYNAMIC_MANAGER_OP_GET) { - if (copy_to_user((void __user *)arg3, &config, - sizeof(config))) { - pr_err("copy dynamic manager config back failed\n"); - return 0; - } - } - - if (ret == 0) { - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("dynamic_manager: prctl reply error\n"); - } - } - return 0; - } - - // Allow root manager to get active managers - if (arg2 == CMD_GET_MANAGERS) { - if (!from_root && !from_manager) { - return 0; - } - - struct manager_list_info manager_info; - int ret = ksu_get_active_managers(&manager_info); - - if (ret == 0) { - if (copy_to_user((void __user *)arg3, &manager_info, - sizeof(manager_info))) { - pr_err("copy manager list failed\n"); - return 0; - } - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("get_managers: prctl reply error\n"); - } - } - return 0; - } - - if (arg2 == CMD_REPORT_EVENT) { - if (!from_root) { - return 0; - } - switch (arg3) { - case EVENT_POST_FS_DATA: { - static bool post_fs_data_lock = false; - if (!post_fs_data_lock) { - post_fs_data_lock = true; - pr_info("post-fs-data triggered\n"); - on_post_fs_data(); - // Initialize UID scanner if enabled - init_uid_scanner(); - // Initializing Dynamic Signatures - ksu_dynamic_manager_init(); - pr_info("Dynamic manager config loaded during post-fs-data\n"); - } - break; - } - case EVENT_BOOT_COMPLETED: { - static bool boot_complete_lock = false; - if (!boot_complete_lock) { - boot_complete_lock = true; - pr_info("boot_complete triggered\n"); - } - break; - } - case EVENT_MODULE_MOUNTED: { - ksu_module_mounted = true; - pr_info("module mounted!\n"); - nuke_ext4_sysfs(); - break; - } - default: - break; - } - return 0; - } - - if (arg2 == CMD_SET_SEPOLICY) { - if (!from_root) { - return 0; - } - if (!handle_sepolicy(arg3, arg4)) { - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("sepolicy: prctl reply error\n"); - } - } - - return 0; - } - - if (arg2 == CMD_CHECK_SAFEMODE) { - if (ksu_is_safe_mode()) { - pr_warn("safemode enabled!\n"); - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("safemode: prctl reply error\n"); - } - } - return 0; - } - - if (arg2 == CMD_GET_ALLOW_LIST || arg2 == CMD_GET_DENY_LIST) { - u32 array[128]; - u32 array_length; - bool success = ksu_get_allow_list(array, &array_length, - arg2 == CMD_GET_ALLOW_LIST); - if (success) { - if (!copy_to_user(arg4, &array_length, - sizeof(array_length)) && - !copy_to_user(arg3, array, - sizeof(u32) * array_length)) { - if (copy_to_user(result, &reply_ok, - sizeof(reply_ok))) { - pr_err("prctl reply error, cmd: %lu\n", - arg2); - } - } else { - pr_err("prctl copy allowlist error\n"); - } - } - return 0; - } - - if (arg2 == CMD_UID_GRANTED_ROOT || arg2 == CMD_UID_SHOULD_UMOUNT) { - uid_t target_uid = (uid_t)arg3; - bool allow = false; - if (arg2 == CMD_UID_GRANTED_ROOT) { - allow = ksu_is_allow_uid(target_uid); - } else if (arg2 == CMD_UID_SHOULD_UMOUNT) { - allow = ksu_uid_should_umount(target_uid); - } else { - pr_err("unknown cmd: %lu\n", arg2); - } - if (!copy_to_user(arg4, &allow, sizeof(allow))) { - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("prctl reply error, cmd: %lu\n", arg2); - } - } else { - pr_err("prctl copy err, cmd: %lu\n", arg2); - } - return 0; - } - - // Checking hook usage - if (arg2 == CMD_HOOK_TYPE) { - const char *hook_type; - -#if defined(CONFIG_KSU_KPROBES_HOOK) - hook_type = "Kprobes"; -#elif defined(CONFIG_KSU_TRACEPOINT_HOOK) - hook_type = "Tracepoint"; -#elif defined(CONFIG_KSU_MANUAL_HOOK) - hook_type = "Manual"; -#else - hook_type = "Unknown"; -#endif - - size_t len = strlen(hook_type) + 1; - if (copy_to_user((void __user *)arg3, hook_type, len)) { - pr_err("hook_type: copy_to_user failed\n"); - return 0; - } - - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("hook_type: prctl reply error\n"); - } - return 0; - } - -#ifdef CONFIG_KPM - // ADD: 添加KPM模块控制 - if (sukisu_is_kpm_control_code(arg2)) { - int res; - - pr_info("KPM: calling before arg2=%d\n", (int)arg2); - - res = sukisu_handle_kpm(arg2, arg3, arg4, arg5); - - return 0; - } -#endif - if (arg2 == CMD_ENABLE_KPM) { - bool KPM_Enabled = IS_ENABLED(CONFIG_KPM); - if (copy_to_user((void __user *)arg3, &KPM_Enabled, - sizeof(KPM_Enabled))) - pr_info("KPM: copy_to_user() failed\n"); - return 0; - } - - // all other cmds are for 'root manager' - if (!from_manager) { - return 0; - } - - // we are already manager - if (arg2 == CMD_GET_APP_PROFILE) { - struct app_profile profile; - if (copy_from_user(&profile, arg3, sizeof(profile))) { - pr_err("copy profile failed\n"); - return 0; - } - - bool success = ksu_get_app_profile(&profile); - if (success) { - if (copy_to_user(arg3, &profile, sizeof(profile))) { - pr_err("copy profile failed\n"); - return 0; - } - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("prctl reply error, cmd: %lu\n", arg2); - } - } - return 0; - } - - if (arg2 == CMD_SET_APP_PROFILE) { - struct app_profile profile; - if (copy_from_user(&profile, arg3, sizeof(profile))) { - pr_err("copy profile failed\n"); - return 0; - } - - // todo: validate the params - if (ksu_set_app_profile(&profile, true)) { - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("prctl reply error, cmd: %lu\n", arg2); - } - } - return 0; - } - - if (arg2 == CMD_IS_SU_ENABLED) { - if (copy_to_user(arg3, &ksu_su_compat_enabled, - sizeof(ksu_su_compat_enabled))) { - pr_err("copy su compat failed\n"); - return 0; - } - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("prctl reply error, cmd: %lu\n", arg2); - } - return 0; - } - if (arg2 == CMD_ENABLE_SU) { - bool enabled = (arg3 != 0); - if (enabled == ksu_su_compat_enabled) { - pr_info("cmd enable su but no need to change.\n"); - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("prctl reply error, cmd: %lu\n", arg2); - } - return 0; - } - if (enabled) { - ksu_sucompat_init(); - } else { - ksu_sucompat_exit(); - } - ksu_su_compat_enabled = enabled; - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("prctl reply error, cmd: %lu\n", arg2); - } - return 0; - } - - // UID Scanner control command - if (arg2 == CMD_ENABLE_UID_SCANNER) { - if (arg3 == 0) { - // Get current status - bool status = ksu_uid_scanner_enabled; - if (copy_to_user((void __user *)arg4, &status, sizeof(status))) { - pr_err("uid_scanner: copy status failed\n"); - return 0; - } - } else if (arg3 == 1) { - // Enable/Disable toggle - bool enabled = (arg4 != 0); - - if (enabled == ksu_uid_scanner_enabled) { - pr_info("uid_scanner: no need to change, already %s\n", - enabled ? "enabled" : "disabled"); - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("uid_scanner: prctl reply error\n"); - } - return 0; - } - - if (enabled) { - // Enable UID scanner - int ret = ksu_throne_comm_init(); - if (ret != 0) { - pr_err("uid_scanner: failed to initialize: %d\n", ret); - return 0; - } - pr_info("uid_scanner: enabled\n"); - } else { - // Disable UID scanner - ksu_throne_comm_exit(); - pr_info("uid_scanner: disabled\n"); - } - - ksu_uid_scanner_enabled = enabled; - ksu_throne_comm_save_state(); - } else if (arg3 == 2) { - // Clear environment (force exit) - ksu_throne_comm_exit(); - ksu_uid_scanner_enabled = false; - ksu_throne_comm_save_state(); - pr_info("uid_scanner: environment cleared\n"); - } - - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("uid_scanner: prctl reply error\n"); - } - return 0; - } - - return 0; } static bool is_appuid(kuid_t uid) @@ -770,13 +283,9 @@ static bool should_umount(struct path *path) return false; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || \ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || \ defined(KSU_HAS_PATH_UMOUNT) -static int ksu_path_umount(struct path *path, int flags) -{ - return path_umount(path, flags); -} -#define ksu_umount_mnt(__unused, path, flags) (ksu_path_umount(path, flags)) +#define ksu_umount_mnt(__unused, path, flags) (path_umount(path, flags)) #else static int ksu_sys_umount(const char *mnt, int flags) { @@ -792,17 +301,17 @@ static int ksu_sys_umount(const char *mnt, int flags) ret = sys_umount(usermnt, flags); // cuz asmlinkage long sys##name #endif set_fs(old_fs); - pr_info("%s was called, ret: %d\n", __func__, ret); return ret; } -#define ksu_umount_mnt(mnt, __unused, flags) \ - ({ \ - int ret; \ - path_put(__unused); \ - ret = ksu_sys_umount(mnt, flags); \ - ret; \ +#define ksu_umount_mnt(mnt, __unused, flags) \ + ({ \ + int ret; \ + path_put(__unused); \ + ret = ksu_sys_umount(mnt, flags); \ + ret; \ }) + #endif static void try_umount(const char *mnt, bool check_mnt, int flags) @@ -834,19 +343,56 @@ static void try_umount(const char *mnt, bool check_mnt, int flags) } } -int ksu_handle_setuid(struct cred *new, const struct cred *old) +static void ksu_do_umount_lists(void) { - // this hook is used for umounting overlayfs for some uid, if there isn't any module mounted, just ignore it! - if (!ksu_module_mounted) { - return 0; + // fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and + // filter the mountpoint whose target is `/data/adb` + try_umount("/odm", true, 0); + try_umount("/system", true, 0); + try_umount("/vendor", true, 0); + try_umount("/product", true, 0); + try_umount("/system_ext", true, 0); + try_umount("/data/adb/modules", false, MNT_DETACH); + + try_umount("/debug_ramdisk", false, MNT_DETACH); + try_umount("/sbin", false, MNT_DETACH); +} + +#if defined(MODULE) || defined(CONFIG_KSU_KPROBES_HOOK) +struct umount_tw { + struct callback_head cb; + const struct cred *old_cred; +}; + +static void umount_tw_func(struct callback_head *cb) +{ + struct umount_tw *tw = container_of(cb, struct umount_tw, cb); + const struct cred *saved = NULL; + if (tw->old_cred) { + saved = override_creds(tw->old_cred); } + ksu_do_umount_lists(); + + if (saved) + revert_creds(saved); + + if (tw->old_cred) + put_cred(tw->old_cred); + + kfree(tw); +} +#endif + +int ksu_handle_setuid(struct cred *new, const struct cred *old) +{ if (!new || !old) { return 0; } kuid_t new_uid = new->uid; kuid_t old_uid = old->uid; + // pr_info("handle_setuid from %d to %d\n", old_uid.val, new_uid.val); if (0 != old_uid.val) { // old process is not root, ignore it. @@ -858,8 +404,52 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old) return 0; } + // if on private space, see if its possibly the manager + if (new_uid.val > 100000 && + new_uid.val % 100000 == ksu_get_manager_uid()) { + ksu_set_manager_uid(new_uid.val); + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + if (ksu_get_manager_uid() == new_uid.val) { + pr_info("install fd for ksu manager(uid=%d)\n", new_uid.val); + ksu_install_fd(); + spin_lock_irq(¤t->sighand->siglock); + ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot); + spin_unlock_irq(¤t->sighand->siglock); + return 0; + } + if (ksu_is_allow_uid(new_uid.val)) { - // pr_info("handle setuid ignore allowed application: %d\n", new_uid.val); + if (current->seccomp.mode == SECCOMP_MODE_FILTER && + current->seccomp.filter) { + spin_lock_irq(¤t->sighand->siglock); + ksu_seccomp_allow_cache(current->seccomp.filter, + __NR_reboot); + spin_unlock_irq(¤t->sighand->siglock); + } + } +#else + if (ksu_is_allow_uid(new_uid.val)) { + spin_lock_irq(¤t->sighand->siglock); + disable_seccomp(current); + spin_unlock_irq(¤t->sighand->siglock); + + if (ksu_get_manager_uid() == new_uid.val) { + pr_info("install fd for ksu manager(uid=%d)\n", new_uid.val); + ksu_install_fd(); + } + + return 0; + } +#endif + + // this hook is used for umounting overlayfs for some uid, if there isn't any module mounted, just ignore it! + if (!ksu_module_mounted) { + return 0; + } + + if (!ksu_kernel_umount_enabled) { return 0; } @@ -874,8 +464,7 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old) // check old process's selinux context, if it is not zygote, ignore it! // because some su apps may setuid to untrusted_app but they are in global mount namespace // when we umount for such process, that is a disaster! - bool is_zygote_child = is_zygote(old->security); - if (!is_zygote_child) { + if (!is_zygote(old->security)) { pr_info("handle umount ignore non zygote child: %d\n", current->pid); return 0; @@ -886,48 +475,35 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old) current->pid); #endif - // fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and - // filter the mountpoint whose target is `/data/adb` - try_umount("/system", true, 0); - try_umount("/vendor", true, 0); - try_umount("/product", true, 0); - try_umount("/system_ext", true, 0); +#if defined(MODULE) || defined(CONFIG_KSU_KPROBES_HOOK) + struct umount_tw *tw; + tw = kmalloc(sizeof(*tw), GFP_ATOMIC); + if (!tw) + return 0; - // try umount modules path - try_umount("/data/adb/modules", false, MNT_DETACH); + tw->old_cred = get_current_cred(); + tw->cb.func = umount_tw_func; - // try umount ksu temp path - try_umount("/debug_ramdisk", false, MNT_DETACH); - try_umount("/sbin", false, MNT_DETACH); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 8) + int err = task_work_add(current, &tw->cb, TWA_RESUME); +#else + int err = task_work_add(current, &tw->cb, true); +#endif + + if (err) { + if (tw->old_cred) { + put_cred(tw->old_cred); + } + kfree(tw); + pr_warn("unmount add task_work failed\n"); + } +#else + ksu_do_umount_lists(); +#endif return 0; } -// kernel 4.4 and 4.9 -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ - defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) -int ksu_key_permission(key_ref_t key_ref, const struct cred *cred, - unsigned perm) -{ - if (init_session_keyring != NULL) { - return 0; - } - if (strcmp(current->comm, "init")) { - // we are only interested in `init` process - return 0; - } - init_session_keyring = cred->session_keyring; - pr_info("kernel_compat: got init_session_keyring\n"); - return 0; -} -#endif - -#ifndef DEVPTS_SUPER_MAGIC -#define DEVPTS_SUPER_MAGIC 0x1cd1 -#endif - -extern int __ksu_handle_devpts(struct inode *inode); // sucompat.c - int ksu_inode_permission(struct inode *inode, int mask) { if (inode && inode->i_sb @@ -938,10 +514,6 @@ int ksu_inode_permission(struct inode *inode, int mask) return 0; } -#ifdef CONFIG_COMPAT -bool ksu_is_compat __read_mostly = false; -#endif - int ksu_bprm_check(struct linux_binprm *bprm) { char *filename = (char *)bprm->filename; @@ -967,52 +539,82 @@ int ksu_bprm_check(struct linux_binprm *bprm) } -#ifdef CONFIG_KSU_LSM_SECURITY_HOOKS -static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3, - unsigned long arg4, unsigned long arg5) +int ksu_handle_sys_reboot(int magic1, int magic2, unsigned int cmd, + void __user **arg) { - ksu_handle_prctl(option, arg2, arg3, arg4, arg5); - return -ENOSYS; + if (magic1 != KSU_INSTALL_MAGIC1) + return 0; + +#ifdef CONFIG_KSU_DEBUG + pr_info("sys_reboot: intercepted call! magic: 0x%x id: %d\n", magic1, + magic2); +#endif + + // Check if this is a request to install KSU fd + if (magic2 == KSU_INSTALL_MAGIC2) { + int fd = ksu_install_fd(); + // downstream: dereference all arg usage! + if (copy_to_user((void __user *)*arg, &fd, sizeof(fd))) { + pr_err("install ksu fd reply err\n"); + } + return 0; + } + + // extensions + + return 0; } -static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry, - struct inode *new_inode, struct dentry *new_dentry) -{ - return ksu_handle_rename(old_dentry, new_dentry); -} - -static int ksu_task_fix_setuid(struct cred *new, const struct cred *old, - int flags) +// -- For old kernel compat? +#if !defined(MODULE) && !defined(CONFIG_KSU_KPROBES_HOOK) +static int ksu_task_fix_setuid(struct cred *new, + const struct cred *old, int flags) { return ksu_handle_setuid(new, old); } -#ifndef MODULE +// kernel 4.4 and 4.9 +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ + defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) +static int ksu_key_permission(key_ref_t key_ref, const struct cred *cred, + unsigned perm) +{ + if (init_session_keyring != NULL) { + return 0; + } + if (strcmp(current->comm, "init")) { + // we are only interested in `init` process + return 0; + } + init_session_keyring = cred->session_keyring; + pr_info("kernel_compat: got init_session_keyring\n"); + return 0; +} +#endif + +#include static struct security_hook_list ksu_hooks[] = { - LSM_HOOK_INIT(task_prctl, ksu_task_prctl), - LSM_HOOK_INIT(inode_rename, ksu_inode_rename), LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid), LSM_HOOK_INIT(inode_permission, ksu_inode_permission), -#ifndef CONFIG_KSU_KPROBES_HOOK + #ifndef CONFIG_KSU_KPROBES_HOOK LSM_HOOK_INIT(bprm_check_security, ksu_bprm_check), #endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) LSM_HOOK_INIT(key_permission, ksu_key_permission) #endif }; #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) -const struct lsm_id ksu_lsmid = { +static const struct lsm_id ksu_lsmid = { .name = "ksu", .id = 912, }; #endif -void __init ksu_lsm_hook_init(void) +static void ksu_lsm_hook_init(void) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) - // https://elixir.bootlin.com/linux/v6.8/source/include/linux/lsm_hooks.h#L120 security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), &ksu_lsmid); #elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), "ksu"); @@ -1020,210 +622,113 @@ void __init ksu_lsm_hook_init(void) // https://elixir.bootlin.com/linux/v4.10.17/source/include/linux/lsm_hooks.h#L1892 security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks)); #endif + pr_info("LSM hooks initialized.\n"); } - #else -// keep renameat_handler for LKM support -static int renameat_handler_pre(struct kprobe *p, struct pt_regs *regs) +static void ksu_lsm_hook_init(void) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) - // https://elixir.bootlin.com/linux/v5.12-rc1/source/include/linux/fs.h - struct renamedata *rd = PT_REGS_PARM1(regs); - struct dentry *old_entry = rd->old_dentry; - struct dentry *new_entry = rd->new_dentry; -#else - struct dentry *old_entry = (struct dentry *)PT_REGS_PARM2(regs); - struct dentry *new_entry = (struct dentry *)PT_REGS_CCALL_PARM4(regs); +} #endif - return ksu_handle_rename(old_entry, new_entry); +// -- For KPROBE and LKM handler +#if defined(MODULE) || defined(CONFIG_KSU_KPROBES_HOOK) +static int reboot_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + struct pt_regs *real_regs = PT_REAL_REGS(regs); + int magic1 = (int)PT_REGS_PARM1(real_regs); + int magic2 = (int)PT_REGS_PARM2(real_regs); + int cmd = (int)PT_REGS_PARM3(real_regs); + void __user **arg = (void __user **)&PT_REGS_SYSCALL_PARM4(real_regs); + + return ksu_handle_sys_reboot(magic1, magic2, cmd, arg); } -static struct kprobe renameat_kp = { - .symbol_name = "vfs_rename", - .pre_handler = renameat_handler_pre, +static struct kprobe reboot_kp = { + .symbol_name = REBOOT_SYMBOL, + .pre_handler = reboot_handler_pre, }; -static int override_security_head(void *head, const void *new_head, size_t len) +// 2. cap_task_fix_setuid hook for handling setuid +static int cap_task_fix_setuid_handler_pre(struct kprobe *p, + struct pt_regs *regs) { - unsigned long base = (unsigned long)head & PAGE_MASK; - unsigned long offset = offset_in_page(head); + struct cred *new = (struct cred *)PT_REGS_PARM1(regs); + const struct cred *old = (const struct cred *)PT_REGS_PARM2(regs); - // this is impossible for our case because the page alignment - // but be careful for other cases! - BUG_ON(offset + len > PAGE_SIZE); - struct page *page = phys_to_page(__pa(base)); - if (!page) { - return -EFAULT; - } + ksu_handle_setuid(new, old); - void *addr = vmap(&page, 1, VM_MAP, PAGE_KERNEL); - if (!addr) { - return -ENOMEM; - } - local_irq_disable(); - memcpy(addr + offset, new_head, len); - local_irq_enable(); - vunmap(addr); return 0; } -static void free_security_hook_list(struct hlist_head *head) +static struct kprobe cap_task_fix_setuid_kp = { + .symbol_name = "cap_task_fix_setuid", + .pre_handler = cap_task_fix_setuid_handler_pre, +}; + +static int ksu_kprobe_init(void) { - struct hlist_node *temp; - struct security_hook_list *entry; + int rc = 0; - if (!head) - return; - - hlist_for_each_entry_safe(entry, temp, head, list) { - hlist_del(&entry->list); - kfree(entry); + // Register reboot kprobe + rc = register_kprobe(&reboot_kp); + if (rc) { + pr_err("reboot kprobe failed: %d\n", rc); + return rc; } + pr_info("reboot kprobe registered successfully\n"); - kfree(head); + // Register cap_task_fix_setuid kprobe + rc = register_kprobe(&cap_task_fix_setuid_kp); + if (rc) { + pr_err("cap_task_fix_setuid kprobe failed: %d\n", rc); + unregister_kprobe(&reboot_kp); + return rc; + } + pr_info("cap_task_fix_setuid kprobe registered successfully\n"); + + return 0; } -struct hlist_head *copy_security_hlist(struct hlist_head *orig) +static void ksu_kprobe_exit(void) { - struct hlist_head *new_head = kmalloc(sizeof(*new_head), GFP_KERNEL); - if (!new_head) - return NULL; - - INIT_HLIST_HEAD(new_head); - - struct security_hook_list *entry; - struct security_hook_list *new_entry; - - hlist_for_each_entry(entry, orig, list) { - new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL); - if (!new_entry) { - free_security_hook_list(new_head); - return NULL; - } - - *new_entry = *entry; - - hlist_add_tail_rcu(&new_entry->list, new_head); - } - - return new_head; + unregister_kprobe(&cap_task_fix_setuid_kp); + unregister_kprobe(&reboot_kp); } -#define LSM_SEARCH_MAX 180 // This should be enough to iterate -static void *find_head_addr(void *security_ptr, int *index) +void __init ksu_core_init(void) { - if (!security_ptr) { - return NULL; - } - struct hlist_head *head_start = - (struct hlist_head *)&security_hook_heads; - - for (int i = 0; i < LSM_SEARCH_MAX; i++) { - struct hlist_head *head = head_start + i; - struct security_hook_list *pos; - hlist_for_each_entry(pos, head, list) { - if (pos->hook.capget == security_ptr) { - if (index) { - *index = i; - } - return head; - } - } + int rc = ksu_kprobe_init(); + if (rc) { + pr_err("ksu_kprobe_init failed: %d\n", rc); } - return NULL; + if (ksu_register_feature_handler(&kernel_umount_handler)) { + pr_err("Failed to register umount feature handler\n"); + } } -#define GET_SYMBOL_ADDR(sym) \ - ({ \ - void *addr = kallsyms_lookup_name(#sym ".cfi_jt"); \ - if (!addr) { \ - addr = kallsyms_lookup_name(#sym); \ - } \ - addr; \ - }) - -#define KSU_LSM_HOOK_HACK_INIT(head_ptr, name, func) \ - do { \ - static struct security_hook_list hook = { \ - .hook = { .name = func } \ - }; \ - hook.head = head_ptr; \ - hook.lsm = "ksu"; \ - struct hlist_head *new_head = copy_security_hlist(hook.head); \ - if (!new_head) { \ - pr_err("Failed to copy security list: %s\n", #name); \ - break; \ - } \ - hlist_add_tail_rcu(&hook.list, new_head); \ - if (override_security_head(hook.head, new_head, \ - sizeof(*new_head))) { \ - free_security_hook_list(new_head); \ - pr_err("Failed to hack lsm for: %s\n", #name); \ - } \ - } while (0) - -void __init ksu_lsm_hook_init(void) +void ksu_core_exit(void) { - void *cap_prctl = GET_SYMBOL_ADDR(cap_task_prctl); - void *prctl_head = find_head_addr(cap_prctl, NULL); - if (prctl_head) { - if (prctl_head != &security_hook_heads.task_prctl) { - pr_warn("prctl's address has shifted!\n"); - } - KSU_LSM_HOOK_HACK_INIT(prctl_head, task_prctl, ksu_task_prctl); - } else { - pr_warn("Failed to find task_prctl!\n"); - } - - int inode_killpriv_index = -1; - void *cap_killpriv = GET_SYMBOL_ADDR(cap_inode_killpriv); - find_head_addr(cap_killpriv, &inode_killpriv_index); - if (inode_killpriv_index < 0) { - pr_warn("Failed to find inode_rename, use kprobe instead!\n"); - register_kprobe(&renameat_kp); - } else { - int inode_rename_index = inode_killpriv_index + - &security_hook_heads.inode_rename - - &security_hook_heads.inode_killpriv; - struct hlist_head *head_start = - (struct hlist_head *)&security_hook_heads; - void *inode_rename_head = head_start + inode_rename_index; - if (inode_rename_head != &security_hook_heads.inode_rename) { - pr_warn("inode_rename's address has shifted!\n"); - } - KSU_LSM_HOOK_HACK_INIT(inode_rename_head, inode_rename, - ksu_inode_rename); - } - void *cap_setuid = GET_SYMBOL_ADDR(cap_task_fix_setuid); - void *setuid_head = find_head_addr(cap_setuid, NULL); - if (setuid_head) { - if (setuid_head != &security_hook_heads.task_fix_setuid) { - pr_warn("setuid's address has shifted!\n"); - } - KSU_LSM_HOOK_HACK_INIT(setuid_head, task_fix_setuid, - ksu_task_fix_setuid); - } else { - pr_warn("Failed to find task_fix_setuid!\n"); - } - smp_mb(); + pr_info("ksu_core_exit\n"); + ksu_kprobe_exit(); + ksu_unregister_feature_handler(KSU_FEATURE_KERNEL_UMOUNT); } -#endif +#else void __init ksu_core_init(void) { ksu_lsm_hook_init(); + + if (ksu_register_feature_handler(&kernel_umount_handler)) { + pr_err("Failed to register umount feature handler\n"); + } } -#else -void __init ksu_core_init(void) -{ - pr_info("ksu_core_init: LSM hooks not in use.\n"); -} -#endif //CONFIG_KSU_LSM_SECURITY_HOOKS void ksu_core_exit(void) { + ksu_unregister_feature_handler(KSU_FEATURE_KERNEL_UMOUNT); ksu_uid_exit(); ksu_throne_comm_exit(); -} \ No newline at end of file +} + +#endif diff --git a/kernel/feature.c b/kernel/feature.c new file mode 100644 index 00000000..2df5c3a4 --- /dev/null +++ b/kernel/feature.c @@ -0,0 +1,176 @@ +#include "feature.h" +#include "klog.h" // IWYU pragma: keep + +#include + +static const struct ksu_feature_handler *feature_handlers[KSU_FEATURE_MAX]; + +static DEFINE_MUTEX(feature_mutex); + +int ksu_register_feature_handler(const struct ksu_feature_handler *handler) +{ + if (!handler) { + pr_err("feature: register handler is NULL\n"); + return -EINVAL; + } + + if (handler->feature_id >= KSU_FEATURE_MAX) { + pr_err("feature: invalid feature_id %u\n", handler->feature_id); + return -EINVAL; + } + + if (!handler->get_handler && !handler->set_handler) { + pr_err("feature: no handler provided for feature %u\n", + handler->feature_id); + return -EINVAL; + } + + mutex_lock(&feature_mutex); + + if (feature_handlers[handler->feature_id]) { + pr_warn("feature: handler for %u already registered, overwriting\n", + handler->feature_id); + } + + feature_handlers[handler->feature_id] = handler; + + pr_info("feature: registered handler for %s (id=%u)\n", + handler->name ? handler->name : "unknown", handler->feature_id); + + mutex_unlock(&feature_mutex); + return 0; +} + +int ksu_unregister_feature_handler(u32 feature_id) +{ + int ret = 0; + + if (feature_id >= KSU_FEATURE_MAX) { + pr_err("feature: invalid feature_id %u\n", feature_id); + return -EINVAL; + } + + mutex_lock(&feature_mutex); + + if (!feature_handlers[feature_id]) { + pr_warn("feature: no handler registered for %u\n", feature_id); + ret = -ENOENT; + goto out; + } + + feature_handlers[feature_id] = NULL; + + pr_info("feature: unregistered handler for id=%u\n", feature_id); + +out: + mutex_unlock(&feature_mutex); + return ret; +} + +int ksu_get_feature(u32 feature_id, u64 *value, bool *supported) +{ + int ret = 0; + const struct ksu_feature_handler *handler; + + if (feature_id >= KSU_FEATURE_MAX) { + pr_err("feature: invalid feature_id %u\n", feature_id); + return -EINVAL; + } + + if (!value || !supported) { + pr_err("feature: invalid parameters\n"); + return -EINVAL; + } + + mutex_lock(&feature_mutex); + + handler = feature_handlers[feature_id]; + + if (!handler) { + *supported = false; + *value = 0; + pr_debug("feature: feature %u not supported\n", feature_id); + goto out; + } + + *supported = true; + + if (!handler->get_handler) { + pr_warn("feature: no get_handler for feature %u\n", feature_id); + ret = -EOPNOTSUPP; + goto out; + } + + ret = handler->get_handler(value); + if (ret) { + pr_err("feature: get_handler for %u failed: %d\n", feature_id, + ret); + } + +out: + mutex_unlock(&feature_mutex); + return ret; +} + +int ksu_set_feature(u32 feature_id, u64 value) +{ + int ret = 0; + const struct ksu_feature_handler *handler; + + if (feature_id >= KSU_FEATURE_MAX) { + pr_err("feature: invalid feature_id %u\n", feature_id); + return -EINVAL; + } + + mutex_lock(&feature_mutex); + + handler = feature_handlers[feature_id]; + + if (!handler) { + pr_err("feature: feature %u not registered\n", feature_id); + ret = -EOPNOTSUPP; + goto out; + } + + if (!handler->set_handler) { + pr_warn("feature: no set_handler for feature %u\n", feature_id); + ret = -EOPNOTSUPP; + goto out; + } + + ret = handler->set_handler(value); + if (ret) { + pr_err("feature: set_handler for %u failed: %d\n", feature_id, + ret); + } + +out: + mutex_unlock(&feature_mutex); + return ret; +} + +void ksu_feature_init(void) +{ + int i; + + for (i = 0; i < KSU_FEATURE_MAX; i++) { + feature_handlers[i] = NULL; + } + + pr_info("%s: feature management initialized\n", __func__); +} + +void ksu_feature_exit(void) +{ + int i; + + mutex_lock(&feature_mutex); + + for (i = 0; i < KSU_FEATURE_MAX; i++) { + feature_handlers[i] = NULL; + } + + mutex_unlock(&feature_mutex); + + pr_info("%s: feature management cleaned up\n", __func__); +} diff --git a/kernel/feature.h b/kernel/feature.h new file mode 100644 index 00000000..a5de137a --- /dev/null +++ b/kernel/feature.h @@ -0,0 +1,35 @@ +#ifndef __KSU_H_FEATURE +#define __KSU_H_FEATURE + +#include + +enum ksu_feature_id { + KSU_FEATURE_SU_COMPAT = 0, + KSU_FEATURE_KERNEL_UMOUNT = 1, + + KSU_FEATURE_MAX +}; + +typedef int (*ksu_feature_get_t)(u64 *value); +typedef int (*ksu_feature_set_t)(u64 value); + +struct ksu_feature_handler { + u32 feature_id; + const char *name; + ksu_feature_get_t get_handler; + ksu_feature_set_t set_handler; +}; + +int ksu_register_feature_handler(const struct ksu_feature_handler *handler); + +int ksu_unregister_feature_handler(u32 feature_id); + +int ksu_get_feature(u32 feature_id, u64 *value, bool *supported); + +int ksu_set_feature(u32 feature_id, u64 value); + +void ksu_feature_init(void); + +void ksu_feature_exit(void); + +#endif // __KSU_H_FEATURE diff --git a/kernel/kernel_compat.c b/kernel/kernel_compat.c index 233981cb..53cd2ff0 100644 --- a/kernel/kernel_compat.c +++ b/kernel/kernel_compat.c @@ -7,16 +7,18 @@ #include #endif #include +#include +#include #include "klog.h" // IWYU pragma: keep -#include "kernel_compat.h" // Add check Huawei Device +#include "kernel_compat.h" -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) #include #include #include -struct key *init_session_keyring = NULL; +struct key *init_session_keyring = NULL; static inline int install_session_keyring(struct key *keyring) { struct cred *new; @@ -82,7 +84,7 @@ void ksu_android_ns_fs_check(void) struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) if (init_session_keyring != NULL && !current_cred()->session_keyring && (current->flags & PF_WQ_WORKER)) { @@ -116,7 +118,7 @@ struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode) ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count, loff_t *pos) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || \ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || \ defined(KSU_OPTIONAL_KERNEL_READ) return kernel_read(p, buf, count, pos); #else @@ -132,7 +134,7 @@ ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count, ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count, loff_t *pos) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || \ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || \ defined(KSU_OPTIONAL_KERNEL_WRITE) return kernel_write(p, buf, count, pos); #else @@ -145,7 +147,7 @@ ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count, #endif } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) || \ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) || \ defined(KSU_OPTIONAL_STRNCPY) long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr, long count) @@ -239,3 +241,63 @@ long ksu_copy_from_user_nofault(void *dst, const void __user *src, size_t size) return 0; #endif } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + +struct action_cache { + DECLARE_BITMAP(allow_native, SECCOMP_ARCH_NATIVE_NR); +#ifdef SECCOMP_ARCH_COMPAT + DECLARE_BITMAP(allow_compat, SECCOMP_ARCH_COMPAT_NR); +#endif +}; + +struct seccomp_filter { + refcount_t refs; + refcount_t users; + bool log; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + bool wait_killable_recv; +#endif + struct action_cache cache; + struct seccomp_filter *prev; + struct bpf_prog *prog; + struct notification *notif; + struct mutex notify_lock; + wait_queue_head_t wqh; +}; + +void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr) +{ + if (!filter) { + return; + } + + if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) { + clear_bit(nr, filter->cache.allow_native); + } + +#ifdef SECCOMP_ARCH_COMPAT + if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) { + clear_bit(nr, filter->cache.allow_compat); + } +#endif +} + +void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr) +{ + if (!filter) { + return; + } + + if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) { + set_bit(nr, filter->cache.allow_native); + } + +#ifdef SECCOMP_ARCH_COMPAT + if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) { + set_bit(nr, filter->cache.allow_compat); + } +#endif +} + +#endif \ No newline at end of file diff --git a/kernel/kernel_compat.h b/kernel/kernel_compat.h index f5c5ff1f..20f12b0a 100644 --- a/kernel/kernel_compat.h +++ b/kernel/kernel_compat.h @@ -3,61 +3,17 @@ #include #include -#include #include "ss/policydb.h" #include "linux/key.h" -#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) -#define kcompat_barrier() do { barrier(); isb(); } while (0) -#else -#define kcompat_barrier() barrier() -#endif - -/* - * Linux 6.8+ does not have LKM support, due to numerous changes on LSM. - * Let's fails if MODULE were defined. - */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) && defined(MODULE) -#error "LKM mode is not supported on Linux 6.8+, aborting build." -#endif - -/** - * list_count_nodes - count the number of nodes in a list - * @head: the head of the list - * - * This function iterates over the list starting from @head and counts - * the number of nodes in the list. It does not modify the list. - * - * Context: Any context. The function is safe to call in any context, - * including interrupt context, as it does not sleep or allocate - * memory. - * - * Return: the number of nodes in the list (excluding the head) - */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) -static inline __maybe_unused size_t list_count_nodes(const struct list_head *head) -{ - const struct list_head *pos; - size_t count = 0; - - if (!head) - return 0; - - list_for_each(pos, head) - count++; - - return count; -} -#endif - /* * Adapt to Huawei HISI kernel without affecting other kernels , * Huawei Hisi Kernel EBITMAP Enable or Disable Flag , * From ss/ebitmap.h */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) && \ - (LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)) || \ - (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) && \ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) && \ + (LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) && \ (LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)) #ifdef HISI_SELINUX_EBITMAP_RO #define CONFIG_IS_HW_HISI @@ -78,11 +34,10 @@ extern long ksu_strncpy_from_user_retry(char *dst, const void __user *unsafe_addr, long count); -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) extern struct key *init_session_keyring; #endif - extern void ksu_android_ns_fs_check(void); extern struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode); @@ -108,6 +63,11 @@ static long ksu_copy_from_user_retry(void *to, return copy_from_user(to, from, count); } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) +extern void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr); +extern void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr); +#endif + #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) #define ksu_access_ok(addr, size) access_ok(addr, size) #else diff --git a/kernel/ksu.c b/kernel/ksu.c index 2f7ff48e..97c6446b 100644 --- a/kernel/ksu.c +++ b/kernel/ksu.c @@ -2,40 +2,19 @@ #include #include #include +#include +#include +#include /* LINUX_VERSION_CODE, KERNEL_VERSION macros */ #include -#include #include "allowlist.h" #include "arch.h" #include "core_hook.h" +#include "feature.h" #include "klog.h" // IWYU pragma: keep #include "ksu.h" #include "throne_tracker.h" -#ifdef CONFIG_KSU_CMDLINE -#include - -// use get_ksu_state()! -unsigned int enable_kernelsu = 1; // enabled by default -static int __init read_kernelsu_state(char *s) -{ - if (s) - enable_kernelsu = simple_strtoul(s, NULL, 0); - return 1; -} -__setup("kernelsu.enabled=", read_kernelsu_state); - -bool get_ksu_state(void) -{ - return enable_kernelsu >= 1; -} -#else -bool get_ksu_state(void) -{ - return true; -} -#endif /* CONFIG_KSU_CMDLINE */ - static struct workqueue_struct *ksu_workqueue; bool ksu_queue_work(struct work_struct *work) @@ -61,6 +40,7 @@ extern void ksu_sucompat_init(void); extern void ksu_sucompat_exit(void); extern void ksu_ksud_init(void); extern void ksu_ksud_exit(void); +extern void ksu_supercalls_init(void); #ifdef CONFIG_KSU_TRACEPOINT_HOOK extern void ksu_trace_register(); extern void ksu_trace_unregister(); @@ -68,32 +48,23 @@ extern void ksu_trace_unregister(); int __init kernelsu_init(void) { - pr_info("kernelsu.enabled=%d\n", (int)get_ksu_state()); - -#ifdef CONFIG_KSU_CMDLINE - if (!get_ksu_state()) { - pr_info_once("drivers is disabled."); - return 0; - } -#endif + pr_info("Initialized on: %s (%s) with driver version: %u\n", + UTS_RELEASE, UTS_MACHINE, KSU_VERSION); #ifdef CONFIG_KSU_DEBUG - pr_alert( - "*************************************************************"); - pr_alert( - "** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **"); - pr_alert( - "** **"); - pr_alert( - "** You are running KernelSU in DEBUG mode **"); - pr_alert( - "** **"); - pr_alert( - "** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **"); - pr_alert( - "*************************************************************"); + pr_alert("*************************************************************"); + pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **"); + pr_alert("** **"); + pr_alert("** You are running KernelSU in DEBUG mode **"); + pr_alert("** **"); + pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **"); + pr_alert("*************************************************************"); #endif + ksu_feature_init(); + + ksu_supercalls_init(); + ksu_core_init(); ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0); @@ -106,8 +77,6 @@ int __init kernelsu_init(void) #ifdef CONFIG_KSU_KPROBES_HOOK ksu_ksud_init(); -#else - pr_debug("init ksu driver\n"); #endif #ifdef CONFIG_KSU_TRACEPOINT_HOOK @@ -124,15 +93,12 @@ int __init kernelsu_init(void) void kernelsu_exit(void) { -#ifdef CONFIG_KSU_CMDLINE - if (!get_ksu_state()) { - return; - } -#endif ksu_allowlist_exit(); ksu_throne_tracker_exit(); + ksu_observer_exit(); + destroy_workqueue(ksu_workqueue); #ifdef CONFIG_KSU_KPROBES_HOOK @@ -146,6 +112,7 @@ void kernelsu_exit(void) ksu_sucompat_exit(); ksu_core_exit(); + ksu_feature_exit(); } module_init(kernelsu_init); @@ -155,10 +122,10 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("weishu"); MODULE_DESCRIPTION("Android KernelSU"); -#define VFS_NS_NAME VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver - +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 13, 0) -MODULE_IMPORT_NS("VFS_NS_NAME"); -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) -MODULE_IMPORT_NS(VFS_NS_NAME); +MODULE_IMPORT_NS("VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver"); +#else +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif #endif diff --git a/kernel/ksu.h b/kernel/ksu.h index 25787cc6..1439c954 100644 --- a/kernel/ksu.h +++ b/kernel/ksu.h @@ -58,13 +58,6 @@ struct dynamic_manager_user_config { char hash[65]; }; -struct manager_list_info { - int count; - struct { - uid_t uid; - int signature_index; - } managers[2]; -}; struct root_profile { int32_t uid; diff --git a/kernel/ksud.c b/kernel/ksud.c index 5be6208c..5b0e5233 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -79,6 +79,7 @@ void on_post_fs_data(void) done = true; pr_info("%s!\n", __func__); ksu_load_allow_list(); + ksu_observer_init(); // sanity check, this may influence the performance stop_input_hook(); @@ -426,7 +427,7 @@ int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, return 0; } -bool ksu_is_safe_mode() +bool ksu_is_safe_mode(void) { static bool safe_mode = false; if (safe_mode) { diff --git a/kernel/manager.h b/kernel/manager.h index 57f43418..454409ae 100644 --- a/kernel/manager.h +++ b/kernel/manager.h @@ -39,4 +39,7 @@ static inline void ksu_invalidate_manager_uid(void) ksu_manager_uid = KSU_INVALID_UID; } -#endif \ No newline at end of file +int ksu_observer_init(void); +void ksu_observer_exit(void); + +#endif diff --git a/kernel/selinux/rules.c b/kernel/selinux/rules.c index 79087be1..c0bcfc86 100644 --- a/kernel/selinux/rules.c +++ b/kernel/selinux/rules.c @@ -157,39 +157,16 @@ void apply_kernelsu_rules(void) #define CMD_TYPE_CHANGE 8 #define CMD_GENFSCON 9 -// keep it! -extern bool ksu_is_compat __read_mostly; - -// armv7l kernel compat -#ifdef CONFIG_64BIT -#define usize u64 -#else -#define usize u32 -#endif - struct sepol_data { - u32 cmd; - u32 subcmd; - usize field_sepol1; - usize field_sepol2; - usize field_sepol3; - usize field_sepol4; - usize field_sepol5; - usize field_sepol6; - usize field_sepol7; -}; - -// ksud 32-bit on arm64 kernel -struct __maybe_unused sepol_data_compat { - u32 cmd; - u32 subcmd; - u32 field_sepol1; - u32 field_sepol2; - u32 field_sepol3; - u32 field_sepol4; - u32 field_sepol5; - u32 field_sepol6; - u32 field_sepol7; + uint32_t cmd; + uint32_t subcmd; + uint64_t field_sepol1; + uint64_t field_sepol2; + uint64_t field_sepol3; + uint64_t field_sepol4; + uint64_t field_sepol5; + uint64_t field_sepol6; + uint64_t field_sepol7; }; static int get_object(char *buf, char __user *user_object, size_t buf_sz, @@ -212,7 +189,7 @@ static int get_object(char *buf, char __user *user_object, size_t buf_sz, // reset avc cache table, otherwise the new rules will not take effect if already denied static void reset_avc_cache(void) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) || \ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) || \ !defined(KSU_COMPAT_USE_SELINUX_STATE) avc_ss_reset(0); selnl_notify_policyload(0); @@ -242,40 +219,22 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4) char __user *sepol1, *sepol2, *sepol3, *sepol4, *sepol5, *sepol6, *sepol7; - if (unlikely(ksu_is_compat)) { - struct sepol_data_compat data_compat; - if (copy_from_user(&data_compat, arg4, - sizeof(struct sepol_data_compat))) { - pr_err("sepol: copy sepol_data failed.\n"); - return -1; - } - pr_info("sepol: running in compat mode!\n"); - sepol1 = compat_ptr(data_compat.field_sepol1); - sepol2 = compat_ptr(data_compat.field_sepol2); - sepol3 = compat_ptr(data_compat.field_sepol3); - sepol4 = compat_ptr(data_compat.field_sepol4); - sepol5 = compat_ptr(data_compat.field_sepol5); - sepol6 = compat_ptr(data_compat.field_sepol6); - sepol7 = compat_ptr(data_compat.field_sepol7); - cmd = data_compat.cmd; - subcmd = data_compat.subcmd; - } else { - struct sepol_data data; - if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) { - pr_err("sepol: copy sepol_data failed.\n"); - return -1; - } - sepol1 = data.field_sepol1; - sepol2 = data.field_sepol2; - sepol3 = data.field_sepol3; - sepol4 = data.field_sepol4; - sepol5 = data.field_sepol5; - sepol6 = data.field_sepol6; - sepol7 = data.field_sepol7; - cmd = data.cmd; - subcmd = data.subcmd; + struct sepol_data data = { 0 }; + if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) { + pr_err("sepol: copy sepol_data failed.\n"); + return -1; } + sepol1 = (char __user *)data.field_sepol1; + sepol2 = (char __user *)data.field_sepol2; + sepol3 = (char __user *)data.field_sepol3; + sepol4 = (char __user *)data.field_sepol4; + sepol5 = (char __user *)data.field_sepol5; + sepol6 = (char __user *)data.field_sepol6; + sepol7 = (char __user *)data.field_sepol7; + cmd = data.cmd; + subcmd = data.subcmd; + mutex_lock(&ksu_rules); db = get_policydb(); diff --git a/kernel/selinux/selinux.c b/kernel/selinux/selinux.c index 431c597a..30aad158 100644 --- a/kernel/selinux/selinux.c +++ b/kernel/selinux/selinux.c @@ -80,7 +80,7 @@ bool getenforce(void) return __is_selinux_enforcing(); } -#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && \ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && \ !defined(KSU_COMPAT_HAS_CURRENT_SID) /* * get the subjective security ID of the current task diff --git a/kernel/selinux/selinux.h b/kernel/selinux/selinux.h index 019204b4..462f6402 100644 --- a/kernel/selinux/selinux.h +++ b/kernel/selinux/selinux.h @@ -4,7 +4,7 @@ #include #include -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) || \ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) || \ defined(KSU_COMPAT_HAS_SELINUX_STATE) #define KSU_COMPAT_USE_SELINUX_STATE #endif diff --git a/kernel/selinux/sepolicy.c b/kernel/selinux/sepolicy.c index 597575fe..acdc45ad 100644 --- a/kernel/selinux/sepolicy.c +++ b/kernel/selinux/sepolicy.c @@ -62,18 +62,18 @@ static bool add_typeattribute(struct policydb *db, const char *type, // rules #define strip_av(effect, invert) ((effect == AVTAB_AUDITDENY) == !invert) -#define ksu_hash_for_each(node_ptr, n_slot, cur) \ - int i; \ - for (i = 0; i < n_slot; ++i) \ +#define ksu_hash_for_each(node_ptr, n_slot, cur) \ + int i; \ + for (i = 0; i < n_slot; ++i) \ for (cur = node_ptr[i]; cur; cur = cur->next) // htable is a struct instead of pointer above 5.8.0: // https://elixir.bootlin.com/linux/v5.8-rc1/source/security/selinux/ss/symtab.h #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) -#define ksu_hashtab_for_each(htab, cur) \ +#define ksu_hashtab_for_each(htab, cur) \ ksu_hash_for_each(htab.htable, htab.size, cur) #else -#define ksu_hashtab_for_each(htab, cur) \ +#define ksu_hashtab_for_each(htab, cur) \ ksu_hash_for_each(htab->htable, htab->size, cur) #endif @@ -84,7 +84,7 @@ static bool add_typeattribute(struct policydb *db, const char *type, #define symtab_insert(s, name, datum) hashtab_insert((s)->table, name, datum) #endif -#define avtab_for_each(avtab, cur) \ +#define avtab_for_each(avtab, cur) \ ksu_hash_for_each(avtab.htable, avtab.nslot, cur); static struct avtab_node *get_avtab_node(struct policydb *db, @@ -552,8 +552,8 @@ static bool add_filename_trans(struct policydb *db, const char *s, } if (trans == NULL) { - trans = (struct filename_trans_datum *)kcalloc(1 ,sizeof(*trans), - GFP_ATOMIC); + trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans), + 1, GFP_ATOMIC); struct filename_trans_key *new_key = (struct filename_trans_key *)kmalloc(sizeof(*new_key), GFP_ATOMIC); @@ -657,27 +657,30 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr) } #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) - struct ebitmap *new_type_attr_map_array = ksu_realloc( - db->type_attr_map_array, value * sizeof(struct ebitmap), - (value - 1) * sizeof(struct ebitmap)); + struct ebitmap *new_type_attr_map_array = + ksu_realloc(db->type_attr_map_array, + value * sizeof(struct ebitmap), + (value - 1) * sizeof(struct ebitmap)); if (!new_type_attr_map_array) { pr_err("add_type: alloc type_attr_map_array failed\n"); return false; } - struct type_datum **new_type_val_to_struct = ksu_realloc( - db->type_val_to_struct, sizeof(*db->type_val_to_struct) * value, - sizeof(*db->type_val_to_struct) * (value - 1)); + struct type_datum **new_type_val_to_struct = + ksu_realloc(db->type_val_to_struct, + sizeof(*db->type_val_to_struct) * value, + sizeof(*db->type_val_to_struct) * (value - 1)); if (!new_type_val_to_struct) { pr_err("add_type: alloc type_val_to_struct failed\n"); return false; } - char **new_val_to_name_types = ksu_realloc( - db->sym_val_to_name[SYM_TYPES], sizeof(char *) * value, - sizeof(char *) * (value - 1)); + char **new_val_to_name_types = + ksu_realloc(db->sym_val_to_name[SYM_TYPES], + sizeof(char *) * value, + sizeof(char *) * (value - 1)); if (!new_val_to_name_types) { pr_err("add_type: alloc val_to_name failed\n"); return false; @@ -700,7 +703,7 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr) } return true; -#elif defined(CONFIG_IS_HW_HISI) && defined(CONFIG_HISI_PMALLOC) +#elif defined(CONFIG_IS_HW_HISI) /* * Huawei use type_attr_map and type_val_to_struct. * And use ebitmap not flex_array. @@ -724,9 +727,10 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr) return false; } - char **new_val_to_name_types = krealloc( - db->sym_val_to_name[SYM_TYPES], - sizeof(char *) * db->symtab[SYM_TYPES].nprim, GFP_KERNEL); + char **new_val_to_name_types = + krealloc(db->sym_val_to_name[SYM_TYPES], + sizeof(char *) * db->symtab[SYM_TYPES].nprim, + GFP_KERNEL); if (!new_val_to_name_types) { pr_err("add_type: alloc val_to_name failed\n"); return false; @@ -832,11 +836,8 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr) if (old_fa) { flex_array_free(old_fa); } - #if defined(CONFIG_IS_HW_HISI) - ebitmap_init(flex_array_get(db->type_attr_map_array, value - 1), HISI_SELINUX_EBITMAP_RO); - #else + ebitmap_init(flex_array_get(db->type_attr_map_array, value - 1)); - #endif ebitmap_set_bit(flex_array_get(db->type_attr_map_array, value - 1), value - 1, 1); @@ -903,21 +904,15 @@ static void add_typeattribute_raw(struct policydb *db, struct type_datum *type, { #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) struct ebitmap *sattr = &db->type_attr_map_array[type->value - 1]; -#elif defined(CONFIG_IS_HW_HISI) && defined(CONFIG_HISI_PMALLOC) +#elif defined(CONFIG_IS_HW_HISI) /* * HISI_SELINUX_EBITMAP_RO is Huawei's unique features. */ struct ebitmap *sattr = &db->type_attr_map[type->value - 1], HISI_SELINUX_EBITMAP_RO; #else - #if defined(CONFIG_IS_HW_HISI) - struct ebitmap *sattr = - flex_array_get(db->type_attr_map_array, type->value - 1), - HISI_SELINUX_EBITMAP_RO; - #else - struct ebitmap *sattr = - flex_array_get(db->type_attr_map_array, type->value - 1); - #endif + struct ebitmap *sattr = + flex_array_get(db->type_attr_map_array, type->value - 1); #endif ebitmap_set_bit(sattr, attr->value - 1, 1); diff --git a/kernel/sucompat.c b/kernel/sucompat.c index a7f27a0a..b06c6439 100644 --- a/kernel/sucompat.c +++ b/kernel/sucompat.c @@ -18,6 +18,7 @@ #include "objsec.h" #include "allowlist.h" #include "arch.h" +#include "feature.h" #include "klog.h" // IWYU pragma: keep #include "ksud.h" #include "kernel_compat.h" @@ -29,8 +30,44 @@ static const char su[] = SU_PATH; static const char ksud_path[] = KSUD_PATH; extern void escape_to_root(void); +void ksu_sucompat_enable(void); +void ksu_sucompat_disable(void); -bool ksu_sucompat_hook_state __read_mostly = true; +static bool ksu_su_compat_enabled __read_mostly = true; + +static int su_compat_feature_get(u64 *value) +{ + *value = ksu_su_compat_enabled ? 1 : 0; + return 0; +} + +static int su_compat_feature_set(u64 value) +{ + bool enable = value != 0; + + if (enable == ksu_su_compat_enabled) { + pr_info("su_compat: no need to change\n"); + return 0; + } + + if (enable) { + ksu_sucompat_enable(); + } else { + ksu_sucompat_disable(); + } + + ksu_su_compat_enabled = enable; + pr_info("su_compat: set to %d\n", enable); + + return 0; +} + +static const struct ksu_feature_handler su_compat_handler = { + .feature_id = KSU_FEATURE_SU_COMPAT, + .name = "su_compat", + .get_handler = su_compat_feature_get, + .set_handler = su_compat_feature_set, +}; static inline void __user *userspace_stack_buffer(const void *d, size_t len) { @@ -55,7 +92,7 @@ static inline char __user *ksud_user_path(void) static inline bool __is_su_allowed(const void *ptr_to_check) { #ifndef CONFIG_KSU_KPROBES_HOOK - if (!ksu_sucompat_hook_state) + if (!ksu_su_compat_enabled) return false; #endif if (likely(!ksu_is_allow_uid(current_uid().val))) @@ -174,6 +211,7 @@ int __ksu_handle_devpts(struct inode *inode) #else struct inode_security_struct *sec = (struct inode_security_struct *)inode->i_security; #endif + if (ksu_devpts_sid && sec) sec->sid = ksu_devpts_sid; @@ -214,7 +252,6 @@ static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs) NULL); } -static struct kprobe *su_kps[6]; static int pts_unix98_lookup_pre(struct kprobe *p, struct pt_regs *regs) { struct inode *inode; @@ -228,6 +265,12 @@ static int pts_unix98_lookup_pre(struct kprobe *p, struct pt_regs *regs) return ksu_handle_devpts(inode); } +#ifdef CONFIG_COMPAT +static struct kprobe *su_kps[6]; +#else +static struct kprobe *su_kps[4]; +#endif + static struct kprobe *init_kprobe(const char *name, kprobe_pre_handler_t handler) { @@ -259,31 +302,55 @@ static void destroy_kprobe(struct kprobe **kp_ptr) } #endif -// sucompat: permited process can execute 'su' to gain root access. -void ksu_sucompat_init(void) +void ksu_sucompat_enable(void) { #ifdef CONFIG_KSU_KPROBES_HOOK su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre); - su_kps[1] = init_kprobe(SYS_EXECVE_COMPAT_SYMBOL, execve_handler_pre); - su_kps[2] = init_kprobe(SYS_FACCESSAT_SYMBOL, faccessat_handler_pre); - su_kps[3] = init_kprobe(SYS_NEWFSTATAT_SYMBOL, newfstatat_handler_pre); - su_kps[4] = init_kprobe(SYS_FSTATAT64_SYMBOL, newfstatat_handler_pre); - su_kps[5] = init_kprobe("pts_unix98_lookup", pts_unix98_lookup_pre); -#else - ksu_sucompat_hook_state = true; - pr_info("ksu_sucompat init\n"); + su_kps[1] = init_kprobe(SYS_FACCESSAT_SYMBOL, faccessat_handler_pre); + su_kps[2] = init_kprobe(SYS_NEWFSTATAT_SYMBOL, newfstatat_handler_pre); + su_kps[3] = init_kprobe("pts_unix98_lookup", pts_unix98_lookup_pre); +#ifdef CONFIG_COMPAT + su_kps[4] = init_kprobe(SYS_EXECVE_COMPAT_SYMBOL, execve_handler_pre); + su_kps[5] = init_kprobe(SYS_FSTATAT64_SYMBOL, newfstatat_handler_pre); +#endif #endif } -void ksu_sucompat_exit(void) +void ksu_sucompat_disable(void) { #ifdef CONFIG_KSU_KPROBES_HOOK int i; for (i = 0; i < ARRAY_SIZE(su_kps); i++) { destroy_kprobe(&su_kps[i]); } -#else - ksu_sucompat_hook_state = false; - pr_info("ksu_sucompat exit\n"); #endif } + +// sucompat: permited process can execute 'su' to gain root access. +void ksu_sucompat_init(void) +{ + if (ksu_register_feature_handler(&su_compat_handler)) { + pr_err("Failed to register su_compat feature handler\n"); + } +#ifdef CONFIG_KSU_KPROBES_HOOK + if (ksu_su_compat_enabled) { + ksu_sucompat_enable(); + } +#else + ksu_su_compat_enabled = true; + pr_info("init sucompat\n"); +#endif +} + +void ksu_sucompat_exit(void) +{ +#ifdef CONFIG_KSU_KPROBES_HOOK + if (ksu_su_compat_enabled) { + ksu_sucompat_disable(); + } +#else + ksu_su_compat_enabled = false; + pr_info("deinit sucompat\n"); +#endif + ksu_unregister_feature_handler(KSU_FEATURE_SU_COMPAT); +} diff --git a/kernel/supercalls.c b/kernel/supercalls.c new file mode 100644 index 00000000..9adab23e --- /dev/null +++ b/kernel/supercalls.c @@ -0,0 +1,695 @@ +#include "supercalls.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "allowlist.h" +#include "feature.h" +#include "klog.h" // IWYU pragma: keep +#include "ksud.h" +#include "manager.h" +#include "sulog.h" +#include "selinux/selinux.h" +#include "kernel_compat.h" +#include "throne_comm.h" +#include "dynamic_manager.h" + +#ifdef CONFIG_KSU_MANUAL_SU +#include "manual_su.h" +#endif + +// Forward declarations from core_hook.c +extern void escape_to_root(void); +extern void nuke_ext4_sysfs(void); +extern bool ksu_module_mounted; +extern int handle_sepolicy(unsigned long arg3, void __user *arg4); +extern void ksu_sucompat_init(void); +extern void ksu_sucompat_exit(void); + +bool ksu_uid_scanner_enabled = false; + +// Permission check functions +bool only_manager(void) +{ + return is_manager(); +} + +bool only_root(void) +{ + return current_uid().val == 0; +} + +bool manager_or_root(void) +{ + return current_uid().val == 0 || is_manager(); +} + +bool always_allow(void) +{ + return true; // No permission check +} + +bool allowed_for_su(void) +{ + bool is_allowed = is_manager() || ksu_is_allow_uid(current_uid().val); +#if __SULOG_GATE + ksu_sulog_report_permission_check(current_uid().val, current->comm, is_allowed); +#endif + return is_allowed; +} + +static void init_uid_scanner(void) +{ + ksu_uid_init(); + do_load_throne_state(NULL); + + if (ksu_uid_scanner_enabled) { + int ret = ksu_throne_comm_init(); + if (ret != 0) { + pr_err("Failed to initialize throne communication: %d\n", ret); + } + } +} + +static int do_grant_root(void __user *arg) +{ + // we already check uid above on allowed_for_su() + + pr_info("allow root for: %d\n", current_uid().val); + escape_to_root(); + + return 0; +} + +static int do_get_info(void __user *arg) +{ + struct ksu_get_info_cmd cmd = {.version = KERNEL_SU_VERSION, .flags = 0}; + +#ifdef MODULE + cmd.flags |= 0x1; +#endif + if (is_manager()) { + cmd.flags |= 0x2; + } + cmd.features = KSU_FEATURE_MAX; + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_version: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +static int do_report_event(void __user *arg) +{ + struct ksu_report_event_cmd cmd; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + return -EFAULT; + } + + switch (cmd.event) { + case EVENT_POST_FS_DATA: { + static bool post_fs_data_lock = false; + if (!post_fs_data_lock) { + post_fs_data_lock = true; + pr_info("post-fs-data triggered\n"); + on_post_fs_data(); + init_uid_scanner(); +#if __SULOG_GATE + ksu_sulog_init(); +#endif + ksu_dynamic_manager_init(); + } + break; + } + case EVENT_BOOT_COMPLETED: { + static bool boot_complete_lock = false; + if (!boot_complete_lock) { + boot_complete_lock = true; + pr_info("boot_complete triggered\n"); + } + break; + } + case EVENT_MODULE_MOUNTED: { + ksu_module_mounted = true; + pr_info("module mounted!\n"); + nuke_ext4_sysfs(); + break; + } + default: + break; + } + + return 0; +} + +static int do_set_sepolicy(void __user *arg) +{ + struct ksu_set_sepolicy_cmd cmd; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + return -EFAULT; + } + + return handle_sepolicy(cmd.cmd, (void __user *)cmd.arg); +} + +static int do_check_safemode(void __user *arg) +{ + struct ksu_check_safemode_cmd cmd; + + cmd.in_safe_mode = ksu_is_safe_mode(); + + if (cmd.in_safe_mode) { + pr_warn("safemode enabled!\n"); + } + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("check_safemode: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +static int do_get_allow_list(void __user *arg) +{ + struct ksu_get_allow_list_cmd cmd; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + return -EFAULT; + } + + bool success = ksu_get_allow_list((int *)cmd.uids, (int *)&cmd.count, true); + + if (!success) { + return -EFAULT; + } + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_allow_list: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +static int do_get_deny_list(void __user *arg) +{ + struct ksu_get_allow_list_cmd cmd; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + return -EFAULT; + } + + bool success = ksu_get_allow_list((int *)cmd.uids, (int *)&cmd.count, false); + + if (!success) { + return -EFAULT; + } + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_deny_list: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +static int do_uid_granted_root(void __user *arg) +{ + struct ksu_uid_granted_root_cmd cmd; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + return -EFAULT; + } + + cmd.granted = ksu_is_allow_uid(cmd.uid); + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("uid_granted_root: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +static int do_uid_should_umount(void __user *arg) +{ + struct ksu_uid_should_umount_cmd cmd; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + return -EFAULT; + } + + cmd.should_umount = ksu_uid_should_umount(cmd.uid); + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("uid_should_umount: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +static int do_get_manager_uid(void __user *arg) +{ + struct ksu_get_manager_uid_cmd cmd; + + cmd.uid = ksu_get_manager_uid(); + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_manager_uid: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +static int do_get_app_profile(void __user *arg) +{ + struct ksu_get_app_profile_cmd cmd; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + pr_err("get_app_profile: copy_from_user failed\n"); + return -EFAULT; + } + + if (!ksu_get_app_profile(&cmd.profile)) { + return -ENOENT; + } + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_app_profile: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +static int do_set_app_profile(void __user *arg) +{ + struct ksu_set_app_profile_cmd cmd; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + pr_err("set_app_profile: copy_from_user failed\n"); + return -EFAULT; + } + + if (!ksu_set_app_profile(&cmd.profile, true)) { +#if __SULOG_GATE + ksu_sulog_report_manager_operation("SET_APP_PROFILE", + current_uid().val, cmd.profile.current_uid); +#endif + return -EFAULT; + } + + return 0; +} + +static int do_get_feature(void __user *arg) +{ + struct ksu_get_feature_cmd cmd; + bool supported; + int ret; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + pr_err("get_feature: copy_from_user failed\n"); + return -EFAULT; + } + + ret = ksu_get_feature(cmd.feature_id, &cmd.value, &supported); + cmd.supported = supported ? 1 : 0; + + if (ret && supported) { + pr_err("get_feature: failed for feature %u: %d\n", cmd.feature_id, ret); + return ret; + } + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_feature: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +static int do_set_feature(void __user *arg) +{ + struct ksu_set_feature_cmd cmd; + int ret; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + pr_err("set_feature: copy_from_user failed\n"); + return -EFAULT; + } + + ret = ksu_set_feature(cmd.feature_id, cmd.value); + if (ret) { + pr_err("set_feature: failed for feature %u: %d\n", cmd.feature_id, ret); + return ret; + } + + return 0; +} + +// 100. GET_FULL_VERSION - Get full version string +static int do_get_full_version(void __user *arg) +{ + struct ksu_get_full_version_cmd cmd = {0}; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) + strscpy(cmd.version_full, KSU_VERSION_FULL, sizeof(cmd.version_full)); +#else + strlcpy(cmd.version_full, KSU_VERSION_FULL, sizeof(cmd.version_full)); +#endif + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_full_version: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +// 101. HOOK_TYPE - Get hook type +static int do_get_hook_type(void __user *arg) +{ + struct ksu_hook_type_cmd cmd = {0}; + const char *type = "Kprobes"; + +#if defined(CONFIG_KSU_TRACEPOINT_HOOK) + type = "Tracepoint"; +#elif defined(CONFIG_KSU_MANUAL_HOOK) + type = "Manual"; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) + strscpy(cmd.hook_type, type, sizeof(cmd.hook_type)); +#else + strlcpy(cmd.hook_type, type, sizeof(cmd.hook_type)); +#endif + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_hook_type: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +// 102. ENABLE_KPM - Check if KPM is enabled +static int do_enable_kpm(void __user *arg) +{ + struct ksu_enable_kpm_cmd cmd; + + cmd.enabled = IS_ENABLED(CONFIG_KPM); + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("enable_kpm: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +static int do_dynamic_manager(void __user *arg) +{ + struct ksu_dynamic_manager_cmd cmd; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + pr_err("dynamic_manager: copy_from_user failed\n"); + return -EFAULT; + } + + int ret = ksu_handle_dynamic_manager(&cmd.config); + if (ret) + return ret; + + if (cmd.config.operation == DYNAMIC_MANAGER_OP_GET && + copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("dynamic_manager: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + +static int do_get_managers(void __user *arg) +{ + struct ksu_get_managers_cmd cmd; + + int ret = ksu_get_active_managers(&cmd.manager_info); + if (ret) + return ret; + + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_managers: copy_from_user failed\n"); + return -EFAULT; + } + + return 0; +} + +static int do_enable_uid_scanner(void __user *arg) +{ + struct ksu_enable_uid_scanner_cmd cmd; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + pr_err("enable_uid_scanner: copy_from_user failed\n"); + return -EFAULT; + } + + switch (cmd.operation) { + case UID_SCANNER_OP_GET_STATUS: { + bool status = ksu_uid_scanner_enabled; + if (copy_to_user((void __user *)cmd.status_ptr, &status, sizeof(status))) { + pr_err("enable_uid_scanner: copy status failed\n"); + return -EFAULT; + } + break; + } + case UID_SCANNER_OP_TOGGLE: { + bool enabled = cmd.enabled; + + if (enabled == ksu_uid_scanner_enabled) { + pr_info("enable_uid_scanner: no need to change, already %s\n", + enabled ? "enabled" : "disabled"); + break; + } + + if (enabled) { + // Enable UID scanner + int ret = ksu_throne_comm_init(); + if (ret != 0) { + pr_err("enable_uid_scanner: failed to initialize: %d\n", ret); + return -EFAULT; + } + pr_info("enable_uid_scanner: enabled\n"); + } else { + // Disable UID scanner + ksu_throne_comm_exit(); + pr_info("enable_uid_scanner: disabled\n"); + } + + ksu_uid_scanner_enabled = enabled; + ksu_throne_comm_save_state(); + break; + } + case UID_SCANNER_OP_CLEAR_ENV: { + // Clear environment (force exit) + ksu_throne_comm_exit(); + ksu_uid_scanner_enabled = false; + ksu_throne_comm_save_state(); + pr_info("enable_uid_scanner: environment cleared\n"); + break; + } + default: + pr_err("enable_uid_scanner: invalid operation\n"); + return -EINVAL; + } + + return 0; +} + +#ifdef CONFIG_KSU_MANUAL_SU +static bool system_uid_check(void) +{ + return current_uid().val <= 2000; +} + +static int do_manual_su(void __user *arg) +{ + struct ksu_manual_su_cmd cmd; + struct manual_su_request request; + int res; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + pr_err("manual_su: copy_from_user failed\n"); + return -EFAULT; + } + + pr_info("manual_su request, option=%d, uid=%d, pid=%d\n", + cmd.option, cmd.target_uid, cmd.target_pid); + + memset(&request, 0, sizeof(request)); + request.target_uid = cmd.target_uid; + request.target_pid = cmd.target_pid; + + if (cmd.option == MANUAL_SU_OP_GENERATE_TOKEN || + cmd.option == MANUAL_SU_OP_ESCALATE) { + memcpy(request.token_buffer, cmd.token_buffer, sizeof(request.token_buffer)); + } + + res = ksu_handle_manual_su_request(cmd.option, &request); + + if (cmd.option == MANUAL_SU_OP_GENERATE_TOKEN && res == 0) { + memcpy(cmd.token_buffer, request.token_buffer, sizeof(cmd.token_buffer)); + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("manual_su: copy_to_user failed\n"); + return -EFAULT; + } + } + + return res; +} +#endif + +// IOCTL handlers mapping table +static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = { + { .cmd = KSU_IOCTL_GRANT_ROOT, .name = "GRANT_ROOT", .handler = do_grant_root, .perm_check = allowed_for_su }, + { .cmd = KSU_IOCTL_GET_INFO, .name = "GET_INFO", .handler = do_get_info, .perm_check = always_allow }, + { .cmd = KSU_IOCTL_REPORT_EVENT, .name = "REPORT_EVENT", .handler = do_report_event, .perm_check = only_root }, + { .cmd = KSU_IOCTL_SET_SEPOLICY, .name = "SET_SEPOLICY", .handler = do_set_sepolicy, .perm_check = only_root }, + { .cmd = KSU_IOCTL_CHECK_SAFEMODE, .name = "CHECK_SAFEMODE", .handler = do_check_safemode, .perm_check = always_allow }, + { .cmd = KSU_IOCTL_GET_ALLOW_LIST, .name = "GET_ALLOW_LIST", .handler = do_get_allow_list, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_GET_DENY_LIST, .name = "GET_DENY_LIST", .handler = do_get_deny_list, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_UID_GRANTED_ROOT, .name = "UID_GRANTED_ROOT", .handler = do_uid_granted_root, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_UID_SHOULD_UMOUNT, .name = "UID_SHOULD_UMOUNT", .handler = do_uid_should_umount, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_GET_MANAGER_UID, .name = "GET_MANAGER_UID", .handler = do_get_manager_uid, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_GET_APP_PROFILE, .name = "GET_APP_PROFILE", .handler = do_get_app_profile, .perm_check = only_manager }, + { .cmd = KSU_IOCTL_SET_APP_PROFILE, .name = "SET_APP_PROFILE", .handler = do_set_app_profile, .perm_check = only_manager }, + { .cmd = KSU_IOCTL_GET_FEATURE, .name = "GET_FEATURE", .handler = do_get_feature, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_SET_FEATURE, .name = "SET_FEATURE", .handler = do_set_feature, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_GET_FULL_VERSION,.name = "GET_FULL_VERSION", .handler = do_get_full_version, .perm_check = always_allow}, + { .cmd = KSU_IOCTL_HOOK_TYPE,.name = "GET_HOOK_TYPE", .handler = do_get_hook_type, .perm_check = manager_or_root}, + { .cmd = KSU_IOCTL_ENABLE_KPM, .name = "GET_ENABLE_KPM", .handler = do_enable_kpm, .perm_check = manager_or_root}, + { .cmd = KSU_IOCTL_DYNAMIC_MANAGER, .name = "SET_DYNAMIC_MANAGER", .handler = do_dynamic_manager, .perm_check = manager_or_root}, + { .cmd = KSU_IOCTL_GET_MANAGERS, .name = "GET_MANAGERS", .handler = do_get_managers, .perm_check = manager_or_root}, + { .cmd = KSU_IOCTL_ENABLE_UID_SCANNER, .name = "SET_ENABLE_UID_SCANNER", .handler = do_enable_uid_scanner, .perm_check = manager_or_root}, +#ifdef CONFIG_KSU_MANUAL_SU + { .cmd = KSU_IOCTL_MANUAL_SU, .name = "MANUAL_SU", .handler = do_manual_su, .perm_check = system_uid_check}, +#endif +#ifdef CONFIG_KPM + { .cmd = KSU_IOCTL_KPM, .name = "KPM_OPERATION", .handler = do_kpm, .perm_check = manager_or_root}, +#endif + { .cmd = 0, .name = NULL, .handler = NULL, .perm_check = NULL} // Sentine +}; + +void ksu_supercalls_init(void) +{ + int i; + + pr_info("KernelSU IOCTL Commands:\n"); + for (i = 0; ksu_ioctl_handlers[i].handler; i++) { + pr_info(" %-18s = 0x%08x\n", ksu_ioctl_handlers[i].name, ksu_ioctl_handlers[i].cmd); + } +} + +static inline void ksu_ioctl_audit(unsigned int cmd, const char *cmd_name, uid_t uid, int ret) +{ +#if __SULOG_GATE + const char *result = (ret == 0) ? "SUCCESS" : + (ret == -EPERM) ? "DENIED" : "FAILED"; + ksu_sulog_report_syscall(uid, NULL, cmd_name, result); +#endif +} + +// IOCTL dispatcher +static long anon_ksu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int i; + +#ifdef CONFIG_KSU_DEBUG + pr_info("ksu ioctl: cmd=0x%x from uid=%d\n", cmd, current_uid().val); +#endif + + for (i = 0; ksu_ioctl_handlers[i].handler; i++) { + if (cmd == ksu_ioctl_handlers[i].cmd) { + // Check permission first + if (ksu_ioctl_handlers[i].perm_check && + !ksu_ioctl_handlers[i].perm_check()) { + pr_warn("ksu ioctl: permission denied for cmd=0x%x uid=%d\n", + cmd, current_uid().val); + ksu_ioctl_audit(cmd, ksu_ioctl_handlers[i].name, + current_uid().val, -EPERM); + return -EPERM; + } + // Execute handler + int ret = ksu_ioctl_handlers[i].handler(argp); + ksu_ioctl_audit(cmd, ksu_ioctl_handlers[i].name, + current_uid().val, ret); + return ret; + } + } + + pr_warn("ksu ioctl: unsupported command 0x%x\n", cmd); + return -ENOTTY; +} + +// File release handler +static int anon_ksu_release(struct inode *inode, struct file *filp) +{ + pr_info("ksu fd released\n"); + return 0; +} + +// File operations structure +static const struct file_operations anon_ksu_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = anon_ksu_ioctl, + .compat_ioctl = anon_ksu_ioctl, + .release = anon_ksu_release, +}; + +// Install KSU fd to current process +int ksu_install_fd(void) +{ + struct file *filp; + int fd; + + // Get unused fd + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) { + pr_err("ksu_install_fd: failed to get unused fd\n"); + return fd; + } + + // Create anonymous inode file + filp = anon_inode_getfile("[ksu_driver]", &anon_ksu_fops, NULL, O_RDWR | O_CLOEXEC); + if (IS_ERR(filp)) { + pr_err("ksu_install_fd: failed to create anon inode file\n"); + put_unused_fd(fd); + return PTR_ERR(filp); + } + + // Install fd + fd_install(fd, filp); + +#if __SULOG_GATE + ksu_sulog_report_permission_check(current_uid().val, current->comm, fd >= 0); +#endif + + pr_info("ksu fd installed: %d for pid %d\n", fd, current->pid); + + return fd; +} \ No newline at end of file diff --git a/kernel/supercalls.h b/kernel/supercalls.h new file mode 100644 index 00000000..6e2e887f --- /dev/null +++ b/kernel/supercalls.h @@ -0,0 +1,157 @@ +#ifndef __KSU_H_SUPERCALLS +#define __KSU_H_SUPERCALLS + +#include +#include +#include "ksu.h" + +#ifdef CONFIG_KPM +#include "kpm/kpm.h" +#endif + +// Magic numbers for reboot hook to install fd +#define KSU_INSTALL_MAGIC1 0xDEADBEEF +#define KSU_INSTALL_MAGIC2 0xCAFEBABE + +// Command structures for ioctl + +struct ksu_become_daemon_cmd { + __u8 token[65]; // Input: daemon token (null-terminated) +}; + +struct ksu_get_info_cmd { + __u32 version; // Output: KERNEL_SU_VERSION + __u32 flags; // Output: flags (bit 0: MODULE mode) + __u32 features; // Output: max feature ID supported +}; + +struct ksu_report_event_cmd { + __u32 event; // Input: EVENT_POST_FS_DATA, EVENT_BOOT_COMPLETED, etc. +}; + +struct ksu_set_sepolicy_cmd { + __u64 cmd; // Input: sepolicy command + __aligned_u64 arg; // Input: sepolicy argument pointer +}; + +struct ksu_check_safemode_cmd { + __u8 in_safe_mode; // Output: true if in safe mode, false otherwise +}; + +struct ksu_get_allow_list_cmd { + __u32 uids[128]; // Output: array of allowed/denied UIDs + __u32 count; // Output: number of UIDs in array + __u8 allow; // Input: true for allow list, false for deny list +}; + +struct ksu_uid_granted_root_cmd { + __u32 uid; // Input: target UID to check + __u8 granted; // Output: true if granted, false otherwise +}; + +struct ksu_uid_should_umount_cmd { + __u32 uid; // Input: target UID to check + __u8 should_umount; // Output: true if should umount, false otherwise +}; + +struct ksu_get_manager_uid_cmd { + __u32 uid; // Output: manager UID +}; + +struct ksu_get_app_profile_cmd { + struct app_profile profile; // Input/Output: app profile structure +}; + +struct ksu_set_app_profile_cmd { + struct app_profile profile; // Input: app profile structure +}; + +struct ksu_get_feature_cmd { + __u32 feature_id; // Input: feature ID (enum ksu_feature_id) + __u64 value; // Output: feature value/state + __u8 supported; // Output: true if feature is supported, false otherwise +}; + +struct ksu_set_feature_cmd { + __u32 feature_id; // Input: feature ID (enum ksu_feature_id) + __u64 value; // Input: feature value/state to set +}; + +// Other command structures +struct ksu_get_full_version_cmd { + char version_full[KSU_FULL_VERSION_STRING]; // Output: full version string +}; + +struct ksu_hook_type_cmd { + char hook_type[32]; // Output: hook type string +}; + +struct ksu_enable_kpm_cmd { + __u8 enabled; // Output: true if KPM is enabled +}; + +struct ksu_dynamic_manager_cmd { + struct dynamic_manager_user_config config; // Input/Output: dynamic manager config +}; + +struct ksu_get_managers_cmd { + struct manager_list_info manager_info; // Output: manager list information +}; + +struct ksu_enable_uid_scanner_cmd { + __u32 operation; // Input: operation type (UID_SCANNER_OP_GET_STATUS, UID_SCANNER_OP_TOGGLE, UID_SCANNER_OP_CLEAR_ENV) + __u32 enabled; // Input: enable or disable (for UID_SCANNER_OP_TOGGLE) + void __user *status_ptr; // Input: pointer to store status (for UID_SCANNER_OP_GET_STATUS) +}; + +#ifdef CONFIG_KSU_MANUAL_SU +struct ksu_manual_su_cmd { + __u32 option; // Input: operation type (MANUAL_SU_OP_GENERATE_TOKEN, MANUAL_SU_OP_ESCALATE, MANUAL_SU_OP_ADD_PENDING) + __u32 target_uid; // Input: target UID + __u32 target_pid; // Input: target PID + char token_buffer[33]; // Input/Output: token buffer +}; +#endif + +// IOCTL command definitions +#define KSU_IOCTL_GRANT_ROOT _IOC(_IOC_NONE, 'K', 1, 0) +#define KSU_IOCTL_GET_INFO _IOC(_IOC_READ, 'K', 2, 0) +#define KSU_IOCTL_REPORT_EVENT _IOC(_IOC_WRITE, 'K', 3, 0) +#define KSU_IOCTL_SET_SEPOLICY _IOC(_IOC_READ|_IOC_WRITE, 'K', 4, 0) +#define KSU_IOCTL_CHECK_SAFEMODE _IOC(_IOC_READ, 'K', 5, 0) +#define KSU_IOCTL_GET_ALLOW_LIST _IOC(_IOC_READ|_IOC_WRITE, 'K', 6, 0) +#define KSU_IOCTL_GET_DENY_LIST _IOC(_IOC_READ|_IOC_WRITE, 'K', 7, 0) +#define KSU_IOCTL_UID_GRANTED_ROOT _IOC(_IOC_READ|_IOC_WRITE, 'K', 8, 0) +#define KSU_IOCTL_UID_SHOULD_UMOUNT _IOC(_IOC_READ|_IOC_WRITE, 'K', 9, 0) +#define KSU_IOCTL_GET_MANAGER_UID _IOC(_IOC_READ, 'K', 10, 0) +#define KSU_IOCTL_GET_APP_PROFILE _IOC(_IOC_READ|_IOC_WRITE, 'K', 11, 0) +#define KSU_IOCTL_SET_APP_PROFILE _IOC(_IOC_WRITE, 'K', 12, 0) +#define KSU_IOCTL_GET_FEATURE _IOC(_IOC_READ|_IOC_WRITE, 'K', 13, 0) +#define KSU_IOCTL_SET_FEATURE _IOC(_IOC_WRITE, 'K', 14, 0) +// Other IOCTL command definitions +#define KSU_IOCTL_GET_FULL_VERSION _IOC(_IOC_READ, 'K', 100, 0) +#define KSU_IOCTL_HOOK_TYPE _IOC(_IOC_READ, 'K', 101, 0) +#define KSU_IOCTL_ENABLE_KPM _IOC(_IOC_READ, 'K', 102, 0) +#define KSU_IOCTL_DYNAMIC_MANAGER _IOC(_IOC_READ|_IOC_WRITE, 'K', 103, 0) +#define KSU_IOCTL_GET_MANAGERS _IOC(_IOC_READ|_IOC_WRITE, 'K', 104, 0) +#define KSU_IOCTL_ENABLE_UID_SCANNER _IOC(_IOC_READ|_IOC_WRITE, 'K', 105, 0) +#ifdef CONFIG_KSU_MANUAL_SU +#define KSU_IOCTL_MANUAL_SU _IOC(_IOC_READ|_IOC_WRITE, 'K', 106, 0) +#endif + +// IOCTL handler types +typedef int (*ksu_ioctl_handler_t)(void __user *arg); +typedef bool (*ksu_perm_check_t)(void); + +// IOCTL command mapping +struct ksu_ioctl_cmd_map { + unsigned int cmd; + const char *name; + ksu_ioctl_handler_t handler; + ksu_perm_check_t perm_check; // Permission check function +}; + +// Install KSU fd to current process +int ksu_install_fd(void); + +#endif // __KSU_H_SUPERCALLS \ No newline at end of file diff --git a/kernel/throne_tracker.c b/kernel/throne_tracker.c index b190fbde..177eed7d 100644 --- a/kernel/throne_tracker.c +++ b/kernel/throne_tracker.c @@ -22,8 +22,7 @@ static uid_t locked_manager_uid = KSU_INVALID_UID; static uid_t locked_dynamic_manager_uid = KSU_INVALID_UID; #define KSU_UID_LIST_PATH "/data/misc/user_uid/uid_list" -#define USER_DATA_PATH "/data/user_de/0" -#define USER_DATA_PATH_LEN 288 +#define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list" struct uid_data { struct list_head list; @@ -74,7 +73,7 @@ static int uid_from_um_list(struct list_head *uid_list) if (!*line) continue; char *uid_str = strsep(&line, " \t"); - char *pkg = line; + char *pkg = line; if (!pkg) continue; while (*pkg == ' ' || *pkg == '\t') ++pkg; if (!*pkg) continue; @@ -180,7 +179,7 @@ static void crown_manager(const char *apk, struct list_head *uid_data, int signa } pr_info("Crowning %s manager: %s (uid=%d, signature_index=%d)\n", - is_dynamic ? "dynamic" : "traditional", pkg, np->uid, signature_index); + is_dynamic ? "dynamic" : "traditional", pkg, np->uid, signature_index); if (is_dynamic) { ksu_add_manager(np->uid, signature_index); @@ -236,155 +235,9 @@ struct my_dir_context { #define FILLDIR_ACTOR_STOP -EINVAL #endif -struct uid_scan_stats { - size_t total_found; - size_t errors_encountered; -}; - -struct user_data_context { - struct dir_context ctx; - struct list_head *uid_list; - struct uid_scan_stats *stats; -}; - -FILLDIR_RETURN_TYPE user_data_actor(struct dir_context *ctx, const char *name, - int namelen, loff_t off, u64 ino, - unsigned int d_type) -{ - struct user_data_context *my_ctx = - container_of(ctx, struct user_data_context, ctx); - - if (!my_ctx || !my_ctx->uid_list) { - return FILLDIR_ACTOR_STOP; - } - - if (!strncmp(name, "..", namelen) || !strncmp(name, ".", namelen)) - return FILLDIR_ACTOR_CONTINUE; - - if (d_type != DT_DIR) - return FILLDIR_ACTOR_CONTINUE; - - if (namelen >= KSU_MAX_PACKAGE_NAME) { - pr_warn("Package name too long: %.*s\n", namelen, name); - if (my_ctx->stats) - my_ctx->stats->errors_encountered++; - return FILLDIR_ACTOR_CONTINUE; - } - - char package_path[USER_DATA_PATH_LEN]; - if (snprintf(package_path, sizeof(package_path), "%s/%.*s", - USER_DATA_PATH, namelen, name) >= sizeof(package_path)) { - pr_err("Path too long for package: %.*s\n", namelen, name); - if (my_ctx->stats) - my_ctx->stats->errors_encountered++; - return FILLDIR_ACTOR_CONTINUE; - } - - struct path path; - int err = kern_path(package_path, LOOKUP_FOLLOW, &path); - if (err) { - pr_debug("Package path lookup failed: %s (err: %d)\n", package_path, err); - if (my_ctx->stats) - my_ctx->stats->errors_encountered++; - return FILLDIR_ACTOR_CONTINUE; - } - -/* -4.11, also backported on lineage common kernel 4.9 !! -int vfs_getattr(const struct path *path, struct kstat *stat, - u32 request_mask, unsigned int query_flags) - -4.10 -int vfs_getattr(struct path *path, struct kstat *stat) - -basically no mask and flags for =< 4.10 - -*/ - struct kstat stat; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0) || defined(KSU_HAS_NEW_VFS_GETATTR) - err = vfs_getattr(&path, &stat, STATX_UID, AT_STATX_SYNC_AS_STAT); -#else - err = vfs_getattr(&path, &stat); -#endif - path_put(&path); - - if (err) { - pr_info("Failed to get attributes for: %s (err: %d)\n", package_path, err); - if (my_ctx->stats) - my_ctx->stats->errors_encountered++; - return FILLDIR_ACTOR_CONTINUE; - } - - uid_t uid = from_kuid(&init_user_ns, stat.uid); - if (uid == (uid_t)-1) { - pr_warn("Invalid UID for package: %.*s\n", namelen, name); - if (my_ctx->stats) - my_ctx->stats->errors_encountered++; - return FILLDIR_ACTOR_CONTINUE; - } - - struct uid_data *data = kzalloc(sizeof(struct uid_data), GFP_ATOMIC); - if (!data) { - pr_err("Failed to allocate memory for package: %.*s\n", namelen, name); - if (my_ctx->stats) - my_ctx->stats->errors_encountered++; - return FILLDIR_ACTOR_CONTINUE; - } - - data->uid = uid; - size_t copy_len = min(namelen, KSU_MAX_PACKAGE_NAME - 1); - strncpy(data->package, name, copy_len); - data->package[copy_len] = '\0'; - - list_add_tail(&data->list, my_ctx->uid_list); - - if (my_ctx->stats) - my_ctx->stats->total_found++; - - pr_info("UserDE UID: Found package: %s, uid: %u\n", data->package, data->uid); - - return FILLDIR_ACTOR_CONTINUE; -} - -static int scan_user_data_for_uids(struct list_head *uid_list) -{ - struct file *dir_file; - struct uid_scan_stats stats = {0}; - int ret = 0; - - if (!uid_list) { - return -EINVAL; - } - - dir_file = ksu_filp_open_compat(USER_DATA_PATH, O_RDONLY, 0); - if (IS_ERR(dir_file)) { - pr_err("UserDE UID: Failed to open %s, err: (%ld)\n", USER_DATA_PATH, PTR_ERR(dir_file)); - return PTR_ERR(dir_file); - } - - struct user_data_context ctx = { - .ctx.actor = user_data_actor, - .uid_list = uid_list, - .stats = &stats - }; - - ret = iterate_dir(dir_file, &ctx.ctx); - filp_close(dir_file, NULL); - - if (stats.errors_encountered > 0) { - pr_warn("Encountered %zu errors while scanning user data directory\n", - stats.errors_encountered); - } - - pr_info("UserDE UID: Scanned %s directory with %zu errors\n", - USER_DATA_PATH, stats.errors_encountered); - - return ret; -} - FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name, - int namelen, loff_t off, u64 ino, - unsigned int d_type) + int namelen, loff_t off, u64 ino, + unsigned int d_type) { struct my_dir_context *my_ctx = container_of(ctx, struct my_dir_context, ctx); @@ -403,20 +256,20 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name, return FILLDIR_ACTOR_CONTINUE; // Skip "." and ".." if (d_type == DT_DIR && namelen >= 8 && !strncmp(name, "vmdl", 4) && - !strncmp(name + namelen - 4, ".tmp", 4)) { - pr_info("Skipping directory: %.*s\n", namelen, name); - return FILLDIR_ACTOR_CONTINUE; // Skip staging package - } + !strncmp(name + namelen - 4, ".tmp", 4)) { + pr_info("Skipping directory: %.*s\n", namelen, name); + return FILLDIR_ACTOR_CONTINUE; // Skip staging package + } if (snprintf(dirpath, DATA_PATH_LEN, "%s/%.*s", my_ctx->parent_dir, - namelen, name) >= DATA_PATH_LEN) { + namelen, name) >= DATA_PATH_LEN) { pr_err("Path too long: %s/%.*s\n", my_ctx->parent_dir, namelen, - name); + name); return FILLDIR_ACTOR_CONTINUE; } if (d_type == DT_DIR && my_ctx->depth > 0 && - (my_ctx->stop && !*my_ctx->stop)) { + (my_ctx->stop && !*my_ctx->stop)) { struct data_path *data = kmalloc(sizeof(struct data_path), GFP_ATOMIC); if (!data) { @@ -506,11 +359,11 @@ void search_manager(const char *path, int depth, struct list_head *uid_data) list_for_each_entry_safe(pos, n, &data_path_list, list) { struct my_dir_context ctx = { .ctx.actor = my_actor, - .data_path_list = &data_path_list, - .parent_dir = pos->dirpath, - .private_data = uid_data, - .depth = pos->depth, - .stop = &stop }; + .data_path_list = &data_path_list, + .parent_dir = pos->dirpath, + .private_data = uid_data, + .depth = pos->depth, + .stop = &stop }; struct file *file; if (!stop) { @@ -565,7 +418,7 @@ static bool is_uid_exist(uid_t uid, char *package, void *data) bool exist = false; list_for_each_entry (np, list, list) { if (np->uid == uid % 100000 && - strncmp(np->package, package, KSU_MAX_PACKAGE_NAME) == 0) { + strncmp(np->package, package, KSU_MAX_PACKAGE_NAME) == 0) { exist = true; break; } @@ -573,35 +426,84 @@ static bool is_uid_exist(uid_t uid, char *package, void *data) return exist; } -extern bool ksu_uid_scanner_enabled; - void track_throne(void) { struct list_head uid_list; struct uid_data *np, *n; + struct file *fp; + char chr = 0; + loff_t pos = 0; + loff_t line_start = 0; + char buf[KSU_MAX_PACKAGE_NAME]; + static bool manager_exist = false; + static bool dynamic_manager_exist = false; + int current_manager_uid = ksu_get_manager_uid() % 100000; // init uid list head INIT_LIST_HEAD(&uid_list); if (ksu_uid_scanner_enabled) { pr_info("Scanning %s directory..\n", KSU_UID_LIST_PATH); + if (uid_from_um_list(&uid_list) == 0) { pr_info("Loaded UIDs from %s success\n", KSU_UID_LIST_PATH); - } else { - pr_warn("%s read failed, falling back to %s\n", KSU_UID_LIST_PATH, USER_DATA_PATH); - if (scan_user_data_for_uids(&uid_list) < 0) - goto out; + goto uid_ready; } - } else { - pr_info("User mode scan disabled, scanning %s\n", USER_DATA_PATH); - if (scan_user_data_for_uids(&uid_list) < 0) - goto out; + + pr_warn("%s read failed, fallback to %s\n", + KSU_UID_LIST_PATH, SYSTEM_PACKAGES_LIST_PATH); } - // check if manager UID exists - bool manager_exist = false; - int current_manager_uid = ksu_get_manager_uid() % 100000; + { + fp = ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_err("%s: open " SYSTEM_PACKAGES_LIST_PATH " failed: %ld\n", __func__, PTR_ERR(fp)); + return; + } + for (;;) { + ssize_t count = + ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos); + if (count != sizeof(chr)) + break; + if (chr != '\n') + continue; + + count = ksu_kernel_read_compat(fp, buf, sizeof(buf), + &line_start); + struct uid_data *data = + kzalloc(sizeof(struct uid_data), GFP_ATOMIC); + if (!data) { + filp_close(fp, 0); + goto out; + } + + char *tmp = buf; + const char *delim = " "; + char *package = strsep(&tmp, delim); + char *uid = strsep(&tmp, delim); + if (!uid || !package) { + pr_err("update_uid: package or uid is NULL!\n"); + break; + } + + u32 res; + if (kstrtou32(uid, 10, &res)) { + pr_err("update_uid: uid parse err\n"); + break; + } + data->uid = res; + strncpy(data->package, package, KSU_MAX_PACKAGE_NAME); + list_add_tail(&data->list, &uid_list); + // reset line start + line_start = pos; + } + + filp_close(fp, 0); + } + +uid_ready: + // first, check if manager_uid exist! list_for_each_entry(np, &uid_list, list) { if (np->uid == current_manager_uid) { manager_exist = true; @@ -610,14 +512,15 @@ void track_throne(void) } if (!manager_exist && locked_manager_uid != KSU_INVALID_UID) { - pr_info("Manager APK removed, unlocking previous UID: %d\n", locked_manager_uid); + pr_info("Manager APK removed, unlock previous UID: %d\n", + locked_manager_uid); ksu_invalidate_manager_uid(); locked_manager_uid = KSU_INVALID_UID; } // Check if the Dynamic Manager exists (only check locked UIDs) - bool dynamic_manager_exist = false; - if (ksu_is_dynamic_manager_enabled() && locked_dynamic_manager_uid != KSU_INVALID_UID) { + if (ksu_is_dynamic_manager_enabled() && + locked_dynamic_manager_uid != KSU_INVALID_UID) { list_for_each_entry(np, &uid_list, list) { if (np->uid == locked_dynamic_manager_uid) { dynamic_manager_exist = true; @@ -626,16 +529,16 @@ void track_throne(void) } if (!dynamic_manager_exist) { - pr_info("Dynamic manager APK removed, unlocking previous UID: %d\n", locked_dynamic_manager_uid); + pr_info("Dynamic manager APK removed, unlock previous UID: %d\n", + locked_dynamic_manager_uid); ksu_remove_manager(locked_dynamic_manager_uid); locked_dynamic_manager_uid = KSU_INVALID_UID; } } bool need_search = !manager_exist; - if (ksu_is_dynamic_manager_enabled() && !dynamic_manager_exist) { + if (ksu_is_dynamic_manager_enabled() && !dynamic_manager_exist) need_search = true; - } if (need_search) { pr_info("Searching for manager(s)...\n"); diff --git a/kernel/throne_tracker_legacy.c b/kernel/throne_tracker_legacy.c deleted file mode 100644 index f244f219..00000000 --- a/kernel/throne_tracker_legacy.c +++ /dev/null @@ -1,609 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "allowlist.h" -#include "klog.h" // IWYU pragma: keep -#include "ksu.h" -#include "manager.h" -#include "throne_tracker.h" -#include "kernel_compat.h" -#include "dynamic_manager.h" -#include "throne_comm.h" - -uid_t ksu_manager_uid = KSU_INVALID_UID; - -#define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list.tmp" -#define KSU_UID_LIST_PATH "/data/misc/user_uid/uid_list" - -struct uid_data { - struct list_head list; - u32 uid; - char package[KSU_MAX_PACKAGE_NAME]; -}; - -// Try read whitelist first, fallback if failed -static int read_uid_whitelist(struct list_head *uid_list) -{ - struct file *fp; - char *file_content = NULL; - char *line, *next_line; - loff_t file_size; - loff_t pos = 0; - int count = 0; - ssize_t bytes_read; - - fp = ksu_filp_open_compat(KSU_UID_LIST_PATH, O_RDONLY, 0); - if (IS_ERR(fp)) { - pr_info("whitelist not found, fallback needed\n"); - return -ENOENT; - } - - file_size = fp->f_inode->i_size; - if (file_size <= 0) { - pr_info("whitelist file is empty\n"); - filp_close(fp, NULL); - return -ENODATA; - } - - file_content = kzalloc(file_size + 1, GFP_ATOMIC); - if (!file_content) { - pr_err("failed to allocate memory for whitelist file (%lld bytes)\n", file_size); - filp_close(fp, NULL); - return -ENOMEM; - } - - bytes_read = ksu_kernel_read_compat(fp, file_content, file_size, &pos); - if (bytes_read != file_size) { - pr_err("failed to read whitelist file: read %zd bytes, expected %lld bytes\n", - bytes_read, file_size); - kfree(file_content); - filp_close(fp, NULL); - return -EIO; - } - - file_content[file_size] = '\0'; - filp_close(fp, NULL); - - pr_info("successfully read whitelist file (%lld bytes), parsing lines...\n", file_size); - - line = file_content; - while (line && *line) { - next_line = strchr(line, '\n'); - if (next_line) { - *next_line = '\0'; - next_line++; - } - - char *trimmed_line = line; - while (*trimmed_line == ' ' || *trimmed_line == '\t' || *trimmed_line == '\r') { - trimmed_line++; - } - - if (strlen(trimmed_line) > 0) { - char *line_copy = trimmed_line; - char *uid_str = strsep(&line_copy, " \t"); - char *package_name = line_copy; - - if (package_name) { - while (*package_name == ' ' || *package_name == '\t') { - package_name++; - } - } - - if (uid_str && package_name && strlen(package_name) > 0) { - u32 uid; - if (!kstrtou32(uid_str, 10, &uid)) { - struct uid_data *data = kzalloc(sizeof(struct uid_data), GFP_ATOMIC); - if (data) { - data->uid = uid; - size_t pkg_len = strlen(package_name); - size_t copy_len = min(pkg_len, (size_t)(KSU_MAX_PACKAGE_NAME - 1)); - strncpy(data->package, package_name, copy_len); - data->package[copy_len] = '\0'; - - list_add_tail(&data->list, uid_list); - count++; - - if (count % 100 == 0) { - pr_info("parsed %d packages so far...\n", count); - } - } else { - pr_err("failed to allocate memory for uid_data\n"); - } - } else { - pr_warn("invalid uid format in line: %s\n", trimmed_line); - } - } else { - pr_warn("invalid line format: %s\n", trimmed_line); - } - } - - line = next_line; - } - - kfree(file_content); - pr_info("successfully loaded %d uids from whitelist\n", count); - return count > 0 ? 0 : -ENODATA; -} - -static int get_pkg_from_apk_path(char *pkg, const char *path) -{ - int len = strlen(path); - if (len >= KSU_MAX_PACKAGE_NAME || len < 1) - return -1; - - const char *last_slash = NULL; - const char *second_last_slash = NULL; - - int i; - for (i = len - 1; i >= 0; i--) { - if (path[i] == '/') { - if (!last_slash) { - last_slash = &path[i]; - } else { - second_last_slash = &path[i]; - break; - } - } - } - - if (!last_slash || !second_last_slash) - return -1; - - const char *last_hyphen = strchr(second_last_slash, '-'); - if (!last_hyphen || last_hyphen > last_slash) - return -1; - - int pkg_len = last_hyphen - second_last_slash - 1; - if (pkg_len >= KSU_MAX_PACKAGE_NAME || pkg_len <= 0) - return -1; - - // Copying the package name - strncpy(pkg, second_last_slash + 1, pkg_len); - pkg[pkg_len] = '\0'; - - return 0; -} - -static void crown_manager(const char *apk, struct list_head *uid_data, - int signature_index) -{ - char pkg[KSU_MAX_PACKAGE_NAME]; - if (get_pkg_from_apk_path(pkg, apk) < 0) { - pr_err("Failed to get package name from apk path: %s\n", apk); - return; - } - - pr_info("manager pkg: %s, signature_index: %d\n", pkg, signature_index); - -#ifdef KSU_MANAGER_PACKAGE - // pkg is `/` - if (strncmp(pkg, KSU_MANAGER_PACKAGE, sizeof(KSU_MANAGER_PACKAGE))) { - pr_info("manager package is inconsistent with kernel build: %s\n", - KSU_MANAGER_PACKAGE); - return; - } -#endif - struct list_head *list = (struct list_head *)uid_data; - struct uid_data *np; - - list_for_each_entry(np, list, list) { - if (strncmp(np->package, pkg, KSU_MAX_PACKAGE_NAME) == 0) { - pr_info("Crowning manager: %s(uid=%d, signature_index=%d)\n", - pkg, np->uid, signature_index); - - // Dynamic Sign index (1) or multi-manager signatures (2+) - if (signature_index == DYNAMIC_SIGN_INDEX || signature_index >= 2) { - ksu_add_manager(np->uid, signature_index); - - if (!ksu_is_manager_uid_valid()) { - ksu_set_manager_uid(np->uid); - } - } else { - ksu_set_manager_uid(np->uid); - } - break; - } - } -} - -#define DATA_PATH_LEN 384 // 384 is enough for /data/app//base.apk - -struct data_path { - char dirpath[DATA_PATH_LEN]; - int depth; - struct list_head list; -}; - -struct apk_path_hash { - unsigned int hash; - bool exists; - struct list_head list; -}; - -static struct list_head apk_path_hash_list; - -struct my_dir_context { - struct dir_context ctx; - struct list_head *data_path_list; - char *parent_dir; - void *private_data; - int depth; - int *stop; -}; -// https://docs.kernel.org/filesystems/porting.html -// filldir_t (readdir callbacks) calling conventions have changed. Instead of returning 0 or -E... it returns bool now. false means "no more" (as -E... used to) and true - "keep going" (as 0 in old calling conventions). Rationale: callers never looked at specific -E... values anyway. -> iterate_shared() instances require no changes at all, all filldir_t ones in the tree converted. -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) -#define FILLDIR_RETURN_TYPE bool -#define FILLDIR_ACTOR_CONTINUE true -#define FILLDIR_ACTOR_STOP false -#else -#define FILLDIR_RETURN_TYPE int -#define FILLDIR_ACTOR_CONTINUE 0 -#define FILLDIR_ACTOR_STOP -EINVAL -#endif -FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name, - int namelen, loff_t off, u64 ino, - unsigned int d_type) -{ - struct my_dir_context *my_ctx = - container_of(ctx, struct my_dir_context, ctx); - char dirpath[DATA_PATH_LEN]; - - if (!my_ctx) { - pr_err("Invalid context\n"); - return FILLDIR_ACTOR_STOP; - } - if (my_ctx->stop && *my_ctx->stop) { - pr_info("Stop searching\n"); - return FILLDIR_ACTOR_STOP; - } - - if (!strncmp(name, "..", namelen) || !strncmp(name, ".", namelen)) - return FILLDIR_ACTOR_CONTINUE; // Skip "." and ".." - - if (d_type == DT_DIR && namelen >= 8 && !strncmp(name, "vmdl", 4) && - !strncmp(name + namelen - 4, ".tmp", 4)) { - pr_info("Skipping directory: %.*s\n", namelen, name); - return FILLDIR_ACTOR_CONTINUE; // Skip staging package - } - - if (snprintf(dirpath, DATA_PATH_LEN, "%s/%.*s", my_ctx->parent_dir, - namelen, name) >= DATA_PATH_LEN) { - pr_err("Path too long: %s/%.*s\n", my_ctx->parent_dir, namelen, - name); - return FILLDIR_ACTOR_CONTINUE; - } - - if (d_type == DT_DIR && my_ctx->depth > 0 && - (my_ctx->stop && !*my_ctx->stop)) { - struct data_path *data = - kmalloc(sizeof(struct data_path), GFP_ATOMIC); - - if (!data) { - pr_err("Failed to allocate memory for %s\n", dirpath); - return FILLDIR_ACTOR_CONTINUE; - } - - strscpy(data->dirpath, dirpath, DATA_PATH_LEN); - data->depth = my_ctx->depth - 1; - list_add_tail(&data->list, my_ctx->data_path_list); - } else { - if ((namelen == 8) && - (strncmp(name, "base.apk", namelen) == 0)) { - struct apk_path_hash *pos, *n; -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) - unsigned int hash = - full_name_hash(dirpath, strlen(dirpath)); -#else - unsigned int hash = - full_name_hash(NULL, dirpath, strlen(dirpath)); -#endif - list_for_each_entry(pos, &apk_path_hash_list, list) { - if (hash == pos->hash) { - pos->exists = true; - return FILLDIR_ACTOR_CONTINUE; - } - } - - int signature_index = -1; - bool is_multi_manager = is_dynamic_manager_apk( - dirpath, &signature_index); - - pr_info("Found new base.apk at path: %s, is_multi_manager: %d, signature_index: %d\n", - dirpath, is_multi_manager, signature_index); - // Check for dynamic sign or multi-manager signatures - if (is_multi_manager && - (signature_index == DYNAMIC_SIGN_INDEX || signature_index >= 2)) { - crown_manager(dirpath, my_ctx->private_data, - signature_index); - - struct apk_path_hash *apk_data = - kmalloc(sizeof(struct apk_path_hash), - GFP_ATOMIC); - - if (apk_data) { - apk_data->hash = hash; - apk_data->exists = true; - list_add_tail(&apk_data->list, - &apk_path_hash_list); - } - - } else if (is_manager_apk(dirpath)) { - crown_manager(dirpath, my_ctx->private_data, 0); - *my_ctx->stop = 1; - - // Manager found, clear APK cache list - list_for_each_entry_safe( - pos, n, &apk_path_hash_list, list) { - list_del(&pos->list); - kfree(pos); - } - } else { - struct apk_path_hash *apk_data = - kmalloc(sizeof(struct apk_path_hash), - GFP_ATOMIC); - if (apk_data) { - apk_data->hash = hash; - apk_data->exists = true; - list_add_tail(&apk_data->list, - &apk_path_hash_list); - } - } - } - } - - return FILLDIR_ACTOR_CONTINUE; -} - -void search_manager(const char *path, int depth, struct list_head *uid_data) -{ - int i, stop = 0; - struct list_head data_path_list; - INIT_LIST_HEAD(&data_path_list); - INIT_LIST_HEAD(&apk_path_hash_list); - unsigned long data_app_magic = 0; - - // Initialize APK cache list - struct apk_path_hash *pos, *n; - list_for_each_entry(pos, &apk_path_hash_list, list) { - pos->exists = false; - } - - // First depth - struct data_path data; - strscpy(data.dirpath, path, DATA_PATH_LEN); - data.depth = depth; - list_add_tail(&data.list, &data_path_list); - - for (i = depth; i >= 0; i--) { - struct data_path *pos, *n; - - list_for_each_entry_safe(pos, n, &data_path_list, list) { - struct my_dir_context ctx = { - .ctx.actor = my_actor, - .data_path_list = &data_path_list, - .parent_dir = pos->dirpath, - .private_data = uid_data, - .depth = pos->depth, - .stop = &stop - }; - struct file *file; - - if (!stop) { - file = ksu_filp_open_compat( - pos->dirpath, O_RDONLY | O_NOFOLLOW, 0); - if (IS_ERR(file)) { - pr_err("Failed to open directory: %s, err: %ld\n", - pos->dirpath, PTR_ERR(file)); - goto skip_iterate; - } - - // grab magic on first folder, which is /data/app - if (!data_app_magic) { - if (file->f_inode->i_sb->s_magic) { - data_app_magic = - file->f_inode->i_sb - ->s_magic; - pr_info("%s: dir: %s got magic! 0x%lx\n", - __func__, pos->dirpath, - data_app_magic); - } else { - filp_close(file, NULL); - goto skip_iterate; - } - } - - if (file->f_inode->i_sb->s_magic != - data_app_magic) { - pr_info("%s: skip: %s magic: 0x%lx expected: 0x%lx\n", - __func__, pos->dirpath, - file->f_inode->i_sb->s_magic, - data_app_magic); - filp_close(file, NULL); - goto skip_iterate; - } - - iterate_dir(file, &ctx.ctx); - filp_close(file, NULL); - } -skip_iterate: - list_del(&pos->list); - if (pos != &data) - kfree(pos); - } - } - - // clear apk_path_hash_list unconditionally - pr_info("search manager: cleanup!\n"); - list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) { - list_del(&pos->list); - kfree(pos); - } -} - -static bool is_uid_exist(uid_t uid, char *package, void *data) -{ - struct list_head *list = (struct list_head *)data; - struct uid_data *np; - - bool exist = false; - list_for_each_entry(np, list, list) { - if (np->uid == uid % 100000 && - strncmp(np->package, package, KSU_MAX_PACKAGE_NAME) == 0) { - exist = true; - break; - } - } - return exist; -} - -void track_throne(void) -{ - struct list_head uid_list; - INIT_LIST_HEAD(&uid_list); - - pr_info("track_throne triggered, attempting whitelist read\n"); - - // Try read whitelist first - int ret = read_uid_whitelist(&uid_list); - - if (ret < 0) { - pr_info("whitelist read failed (%d), request userspace scan\n", ret); - - // Request userspace to rescan - ksu_request_userspace_scan(); - - // fallback to packages.list method - struct file *fp = ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0); - if (IS_ERR(fp)) { - pr_err("%s: open " SYSTEM_PACKAGES_LIST_PATH " failed: %ld\n", - __func__, PTR_ERR(fp)); - goto out; - } - - char chr = 0; - loff_t pos = 0; - loff_t line_start = 0; - char buf[KSU_MAX_PACKAGE_NAME]; - size_t fallback_count = 0; - - for (;;) { - ssize_t count = - ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos); - if (count != sizeof(chr)) - break; - if (chr != '\n') - continue; - - count = ksu_kernel_read_compat(fp, buf, sizeof(buf), - &line_start); - - struct uid_data *data = - kzalloc(sizeof(struct uid_data), GFP_ATOMIC); - if (!data) { - filp_close(fp, 0); - goto out; - } - - char *tmp = buf; - const char *delim = " "; - char *package = strsep(&tmp, delim); - char *uid = strsep(&tmp, delim); - if (!uid || !package) { - pr_err("update_uid: package or uid is NULL!\n"); - kfree(data); - break; - } - - u32 res; - if (kstrtou32(uid, 10, &res)) { - pr_err("update_uid: uid parse err\n"); - kfree(data); - break; - } - data->uid = res; - strncpy(data->package, package, KSU_MAX_PACKAGE_NAME); - list_add_tail(&data->list, &uid_list); - fallback_count++; - - // reset line start - line_start = pos; - } - filp_close(fp, 0); - pr_info("Loaded %zu packages from packages.list fallback\n", fallback_count); - } else { - pr_info("loaded uids from whitelist successfully\n"); - } - - // now update uid list - struct uid_data *np; - struct uid_data *n; - - // first, check if manager_uid exist! - bool manager_exist = false; - bool dynamic_manager_exist = false; - - list_for_each_entry(np, &uid_list, list) { - // if manager is installed in work profile, the uid in packages.list is still equals main profile - // don't delete it in this case! - int manager_uid = ksu_get_manager_uid() % 100000; - if (np->uid == manager_uid) { - manager_exist = true; - break; - } - } - - // Check for dynamic managers - if (!dynamic_manager_exist && ksu_is_dynamic_manager_enabled()) { - list_for_each_entry(np, &uid_list, list) { - // Check if this uid is a dynamic manager (not the traditional manager) - if (ksu_is_any_manager(np->uid) && - np->uid != ksu_get_manager_uid()) { - dynamic_manager_exist = true; - break; - } - } - } - - if (!manager_exist) { - if (ksu_is_manager_uid_valid()) { - pr_info("manager is uninstalled, invalidate it!\n"); - ksu_invalidate_manager_uid(); - goto prune; - } - pr_info("Searching manager...\n"); - search_manager("/data/app", 2, &uid_list); - pr_info("Search manager finished\n"); - } else if (!dynamic_manager_exist && ksu_is_dynamic_manager_enabled()) { - // Always perform search when called from dynamic manager rescan - pr_info("Dynamic sign enabled, Searching manager...\n"); - search_manager("/data/app", 2, &uid_list); - pr_info("Search Dynamic sign manager finished\n"); - } - -prune: - // then prune the allowlist - ksu_prune_allowlist(is_uid_exist, &uid_list); -out: - // free uid_list - list_for_each_entry_safe(np, n, &uid_list, list) { - list_del(&np->list); - kfree(np); - } -} - -void ksu_throne_tracker_init(void) -{ - // nothing to do -} - -void ksu_throne_tracker_exit(void) -{ - // nothing to do -}