kernel, ksud, manager: New supercalls implementations

* This commit squashes new supercall impl:
3138651a38..562a3b9be7

Thanks to these people below:

Official KernelSU:
Co-authored-by: Wang Han <416810799@qq.com>
Co-authored-by: weishu <twsxtd@gmail.com>
Co-authored-by: Ylarod <me@ylarod.cn>
Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com>

xxKSU maintainer:
Co-authored-by: backslashxx <118538522+backslashxx@users.noreply.github.com>
MMRL maintainer:
Co-authored-by: Der_Googler <54764558+dergoogler@users.noreply.github.com>
KSUN maintainer:
Co-authored-by: Rifat Azad <33044977+rifsxd@users.noreply.github.com>
KOWSU maintainer:
Co-authored-by: KOWX712 <leecc0503@gmail.com>
This commit is contained in:
ShirkNeko
2025-11-06 03:54:44 +08:00
parent c55a918957
commit 68f3be2cbe
23 changed files with 1804 additions and 2218 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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"

File diff suppressed because it is too large Load Diff

176
kernel/feature.c Normal file
View File

@@ -0,0 +1,176 @@
#include "feature.h"
#include "klog.h" // IWYU pragma: keep
#include <linux/mutex.h>
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__);
}

35
kernel/feature.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef __KSU_H_FEATURE
#define __KSU_H_FEATURE
#include <linux/types.h>
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

View File

@@ -7,16 +7,18 @@
#include <linux/sched.h>
#endif
#include <linux/uaccess.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#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 <linux/key.h>
#include <linux/errno.h>
#include <linux/cred.h>
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

View File

@@ -3,61 +3,17 @@
#include <linux/fs.h>
#include <linux/version.h>
#include <linux/cred.h>
#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

View File

@@ -2,40 +2,19 @@
#include <linux/fs.h>
#include <linux/kobject.h>
#include <linux/module.h>
#include <generated/utsrelease.h>
#include <generated/compile.h>
#include <linux/version.h> /* LINUX_VERSION_CODE, KERNEL_VERSION macros */
#include <linux/workqueue.h>
#include <linux/version.h>
#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 <linux/init.h>
// 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

View File

@@ -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;

View File

@@ -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) {

View File

@@ -39,4 +39,7 @@ static inline void ksu_invalidate_manager_uid(void)
ksu_manager_uid = KSU_INVALID_UID;
}
int ksu_observer_init(void);
void ksu_observer_exit(void);
#endif

View File

@@ -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();

View File

@@ -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

View File

@@ -4,7 +4,7 @@
#include <linux/types.h>
#include <linux/version.h>
#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

View File

@@ -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);

View File

@@ -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);
}

695
kernel/supercalls.c Normal file
View File

@@ -0,0 +1,695 @@
#include "supercalls.h"
#include <linux/anon_inodes.h>
#include <linux/capability.h>
#include <linux/cred.h>
#include <linux/err.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#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;
}

157
kernel/supercalls.h Normal file
View File

@@ -0,0 +1,157 @@
#ifndef __KSU_H_SUPERCALLS
#define __KSU_H_SUPERCALLS
#include <linux/types.h>
#include <linux/ioctl.h>
#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

View File

@@ -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");

View File

@@ -1,609 +0,0 @@
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/version.h>
#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 `/<real package>`
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/<package>/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
}