fix lot (#518)
* refact: use feature subsystem * use 64bit feature * fix * add fixme * add feature max to get_info * use 32bit feature id * allow root to get/set feature * more clean perm_check functions * fix * add feature command to ksud kernel: do not expose perm checker * fix security_task_fix_setuid_handler_pre * add android16-6.12 ci * manager: add kernel_umount switch Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com> * manager: Reinstate the LKM selection function * kernel: add name and print command value - Optimise sulog log display Co-authored-by: Ylarod <me@ylarod.cn> Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com> * fix * ksud: clippy --------- Co-authored-by: Ylarod <me@ylarod.cn> Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com> Co-authored-by: weishu <twsxtd@gmail.com>
This commit is contained in:
137
.github/workflows/avd-kernel.yml
vendored
137
.github/workflows/avd-kernel.yml
vendored
@@ -1,137 +0,0 @@
|
||||
name: GKI Kernel Build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
version_name:
|
||||
required: true
|
||||
type: string
|
||||
description: >
|
||||
With SUBLEVEL of kernel,
|
||||
for example: android12-5.10.66
|
||||
arch:
|
||||
required: true
|
||||
type: string
|
||||
description: >
|
||||
Build arch: aarch64/x86_64
|
||||
debug:
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
manifest_name:
|
||||
required: false
|
||||
type: string
|
||||
description: >
|
||||
Local repo manifest xml path,
|
||||
typically for AVD kernel build.
|
||||
secrets:
|
||||
BOOT_SIGN_KEY:
|
||||
required: false
|
||||
CHAT_ID:
|
||||
required: false
|
||||
BOT_TOKEN:
|
||||
required: false
|
||||
MESSAGE_THREAD_ID:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build ${{ inputs.version_name }}
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Maximize build space
|
||||
uses: easimon/maximize-build-space@master
|
||||
with:
|
||||
root-reserve-mb: 8192
|
||||
temp-reserve-mb: 2048
|
||||
remove-dotnet: 'true'
|
||||
remove-android: 'true'
|
||||
remove-haskell: 'true'
|
||||
remove-codeql: 'true'
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
path: KernelSU
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup need_upload
|
||||
id: need_upload
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
||||
echo "UPLOAD=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Setup kernel source
|
||||
run: |
|
||||
echo "Free space:"
|
||||
df -h
|
||||
cd $GITHUB_WORKSPACE
|
||||
sudo apt-get install repo -y
|
||||
mkdir android-kernel && cd android-kernel
|
||||
repo init --depth=1 -u https://android.googlesource.com/kernel/manifest -m "$GITHUB_WORKSPACE/KernelSU/.github/manifests/${{ inputs.manifest_name }}" --repo-rev=v2.16
|
||||
repo --version
|
||||
repo --trace sync -c -j$(nproc --all) --no-tags
|
||||
df -h
|
||||
|
||||
- name: Setup KernelSU
|
||||
env:
|
||||
PATCH_PATH: ${{ inputs.patch_path }}
|
||||
IS_DEBUG_KERNEL: ${{ inputs.debug }}
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/android-kernel
|
||||
echo "[+] KernelSU setup"
|
||||
GKI_ROOT=$(pwd)
|
||||
echo "[+] GKI_ROOT: $GKI_ROOT"
|
||||
echo "[+] Copy KernelSU driver to $GKI_ROOT/common/drivers"
|
||||
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $GKI_ROOT/common/drivers/kernelsu
|
||||
echo "[+] Add KernelSU driver to Makefile"
|
||||
DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile
|
||||
DRIVER_KCONFIG=$GKI_ROOT/common/drivers/Kconfig
|
||||
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE"
|
||||
grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG"
|
||||
echo "[+] Apply KernelSU patches"
|
||||
cd $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/$PATCH_PATH/*.patch || echo "[-] No patch found"
|
||||
|
||||
if [ "$IS_DEBUG_KERNEL" = "true" ]; then
|
||||
echo "[+] Enable debug features for kernel"
|
||||
printf "\nccflags-y += -DCONFIG_KSU_DEBUG\n" >> $GITHUB_WORKSPACE/KernelSU/kernel/Makefile
|
||||
fi
|
||||
repo status
|
||||
echo "[+] KernelSU setup done."
|
||||
cd $GITHUB_WORKSPACE/KernelSU
|
||||
VERSION=$(($(git rev-list --count HEAD) + 10200))
|
||||
echo "VERSION: $VERSION"
|
||||
echo "kernelsu_version=$VERSION" >> $GITHUB_ENV
|
||||
|
||||
- name: Make working directory clean to avoid dirty
|
||||
working-directory: android-kernel
|
||||
run: |
|
||||
rm common/android/abi_gki_protected_exports_* || echo "No protected exports!"
|
||||
git config --global user.email "bot@kernelsu.org"
|
||||
git config --global user.name "KernelSUBot"
|
||||
cd common/ && git add -A && git commit -a -m "Add KernelSU"
|
||||
repo status
|
||||
|
||||
- name: Build kernel
|
||||
working-directory: android-kernel
|
||||
run: |
|
||||
if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then
|
||||
export KSU_EXPECTED_SIZE=${{ vars.EXPECTED_SIZE }}
|
||||
export KSU_EXPECTED_HASH=${{ vars.EXPECTED_HASH }}
|
||||
fi
|
||||
tools/bazel run --config=fast --config=stamp --lto=thin //common-modules/virtual-device:virtual_device_${{ inputs.arch }}_dist -- --dist_dir=dist
|
||||
NAME=kernel-${{ inputs.arch }}-avd-${{ inputs.version_name }}-${{ env.kernelsu_version }}
|
||||
TARGET_IMAGE=dist/bzImage
|
||||
if [ ! -e $TARGET_IMAGE ]; then
|
||||
TARGET_IMAGE=dist/Image
|
||||
fi
|
||||
mv $TARGET_IMAGE $NAME
|
||||
echo "file_path=android-kernel/$NAME" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload Kernel
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kernel-${{ inputs.arch }}-avd-${{ inputs.version_name }}-${{ env.kernelsu_version }}
|
||||
path: "${{ env.file_path }}"
|
||||
74
.github/workflows/build-lkm-local.yml
vendored
74
.github/workflows/build-lkm-local.yml
vendored
@@ -1,74 +0,0 @@
|
||||
name: Build LKM for KernelSU Local
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
upload:
|
||||
required: true
|
||||
type: boolean
|
||||
default: true
|
||||
description: "Whether to upload to branch"
|
||||
secrets:
|
||||
# username:github_pat
|
||||
TOKEN:
|
||||
required: true
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
upload:
|
||||
required: true
|
||||
type: boolean
|
||||
default: true
|
||||
description: "Whether to upload to branch"
|
||||
jobs:
|
||||
build-lkm:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- version: "android12-5.10"
|
||||
sub_level: 236
|
||||
os_patch_level: 2025-05
|
||||
- version: "android13-5.10"
|
||||
sub_level: 234
|
||||
os_patch_level: 2025-03
|
||||
- version: "android13-5.15"
|
||||
sub_level: 178
|
||||
os_patch_level: 2025-03
|
||||
- version: "android14-5.15"
|
||||
sub_level: 178
|
||||
os_patch_level: 2025-03
|
||||
- version: "android14-6.1"
|
||||
sub_level: 134
|
||||
os_patch_level: 2025-05
|
||||
- version: "android15-6.6"
|
||||
sub_level: 87
|
||||
os_patch_level: 2025-05
|
||||
# uses: ./.github/workflows/gki-kernel-mock.yml when debugging
|
||||
uses: ./.github/workflows/gki-kernel-local.yml
|
||||
with:
|
||||
version: ${{ matrix.version }}
|
||||
version_name: ${{ matrix.version }}.${{ matrix.sub_level }}
|
||||
tag: ${{ matrix.version }}-${{ matrix.os_patch_level }}
|
||||
os_patch_level: ${{ matrix.os_patch_level }}
|
||||
build_lkm: true
|
||||
|
||||
push-to-branch:
|
||||
needs: [build-lkm]
|
||||
runs-on: self-hosted
|
||||
if: ${{ inputs.upload }}
|
||||
steps:
|
||||
- name: Download all workflow run artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: bin/
|
||||
merge-multiple: true
|
||||
- name: Push to branch LKM
|
||||
run: |
|
||||
cd bin
|
||||
git config --global init.defaultBranch lkm
|
||||
git init
|
||||
git remote add origin https://${{ secrets.TOKEN }}@github.com/${{ github.repository }}
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
find . -type f
|
||||
git add .
|
||||
git commit -m "Upload LKM from ${{ github.sha }}" -m "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||
git push --force --set-upstream origin lkm
|
||||
3
.github/workflows/build-lkm.yml
vendored
3
.github/workflows/build-lkm.yml
vendored
@@ -41,6 +41,9 @@ jobs:
|
||||
- version: "android15-6.6"
|
||||
sub_level: 98
|
||||
os_patch_level: 2025-09
|
||||
- version: "android16-6.12"
|
||||
sub_level: 38
|
||||
os_patch_level: 2025-09
|
||||
# uses: ./.github/workflows/gki-kernel-mock.yml when debugging
|
||||
uses: ./.github/workflows/gki-kernel.yml
|
||||
with:
|
||||
|
||||
245
.github/workflows/build-manager-manual.yml
vendored
245
.github/workflows/build-manager-manual.yml
vendored
@@ -1,245 +0,0 @@
|
||||
name: Build Manager Manual
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
build_lkm:
|
||||
required: true
|
||||
type: choice
|
||||
default: "auto"
|
||||
options:
|
||||
- "true"
|
||||
- "false"
|
||||
- "auto"
|
||||
description: "Whether to build lkm"
|
||||
upload_lkm:
|
||||
required: true
|
||||
type: boolean
|
||||
default: true
|
||||
description: "Whether to upload lkm"
|
||||
jobs:
|
||||
check-build-lkm:
|
||||
runs-on: self-hosted
|
||||
outputs:
|
||||
build_lkm: ${{ steps.check-build.outputs.build_lkm }}
|
||||
upload_lkm: ${{ steps.check-build.outputs.upload_lkm }}
|
||||
steps:
|
||||
- name: check build
|
||||
id: check-build
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ "${{ inputs.build_lkm }}" != "auto" ]; then
|
||||
kernel_changed="${{ inputs.build_lkm }}"
|
||||
else
|
||||
kernel_changed=true
|
||||
mkdir tmp
|
||||
cd tmp
|
||||
git config --global init.defaultBranch bot
|
||||
git config --global user.name 'Bot'
|
||||
git config --global user.email 'bot@github.shirkneko.io'
|
||||
git init .
|
||||
git remote add origin https://github.com/${{ github.repository }}
|
||||
CURRENT_COMMIT="${{ github.event.head_commit.id }}"
|
||||
git fetch origin $CURRENT_COMMIT --depth=1
|
||||
git fetch origin lkm --depth=1
|
||||
LKM_COMMIT="$(git log --format=%B -n 1 origin/lkm | head -n 1)"
|
||||
LKM_COMMIT="${LKM_COMMIT#Upload LKM from }"
|
||||
LKM_COMMIT=$(echo "$LKM_COMMIT" | tr -d '[:space:]')
|
||||
echo "LKM_COMMIT=$LKM_COMMIT"
|
||||
git fetch origin "$LKM_COMMIT" --depth=1
|
||||
git diff --quiet "$LKM_COMMIT" "$CURRENT_COMMIT" -- kernel :!kernel/setup.sh .github/workflows/build-lkm-local.yml .github/workflows/build-kernel-*.yml && kernel_changed=false
|
||||
cd ..
|
||||
rm -rf tmp
|
||||
fi
|
||||
if [ "${{ github.event_name }}" == "push" ] && [ "${{ github.ref }}" == 'refs/heads/main' ]; then
|
||||
need_upload=true
|
||||
elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
|
||||
need_upload="${{ inputs.upload_lkm }}"
|
||||
else
|
||||
need_upload=false
|
||||
fi
|
||||
echo "kernel changed: $kernel_changed"
|
||||
echo "need upload: $need_upload"
|
||||
echo "build_lkm=$kernel_changed" >> "$GITHUB_OUTPUT"
|
||||
echo "upload_lkm=$need_upload" >> "$GITHUB_OUTPUT"
|
||||
|
||||
build-lkm:
|
||||
needs: check-build-lkm
|
||||
uses: ./.github/workflows/build-lkm-local.yml
|
||||
if: ${{ needs.check-build-lkm.outputs.build_lkm == 'true' }}
|
||||
with:
|
||||
upload: ${{ needs.check-build-lkm.outputs.upload_lkm == 'true' }}
|
||||
secrets: inherit
|
||||
build-susfs:
|
||||
if: ${{ always() }}
|
||||
needs: [ check-build-lkm, build-lkm ]
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- target: aarch64-linux-android
|
||||
os: ubuntu-latest
|
||||
uses: ./.github/workflows/susfs.yml
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
os: ${{ matrix.os }}
|
||||
|
||||
build-kpmmgr:
|
||||
if: ${{ always() }}
|
||||
needs: [ check-build-lkm, build-lkm ]
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- target: aarch64-linux-android
|
||||
os: ubuntu-latest
|
||||
uses: ./.github/workflows/kpmmgr.yml
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
os: ${{ matrix.os }}
|
||||
|
||||
build-ksud:
|
||||
if: ${{ always() }}
|
||||
needs: [ check-build-lkm, build-lkm ]
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- target: aarch64-linux-android
|
||||
os: ubuntu-latest
|
||||
- target: x86_64-linux-android
|
||||
os: ubuntu-latest
|
||||
- target: armv7-linux-androideabi
|
||||
os: ubuntu-latest
|
||||
uses: ./.github/workflows/ksud.yml
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
os: ${{ matrix.os }}
|
||||
pack_lkm: true
|
||||
pull_lkm: ${{ needs.check-build-lkm.outputs.build_lkm != 'true' }}
|
||||
|
||||
build-manager:
|
||||
if: ${{ always() }}
|
||||
needs: build-ksud
|
||||
runs-on: self-hosted
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ./manager
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup need_upload
|
||||
id: need_upload
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
||||
echo "UPLOAD=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Write key
|
||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref == 'refs/heads/susfs' || github.ref_type == 'tag' }}
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.KEYSTORE }}" ]; then
|
||||
{
|
||||
echo KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}'
|
||||
echo KEY_ALIAS='${{ secrets.KEY_ALIAS }}'
|
||||
echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}'
|
||||
echo KEYSTORE_FILE='key.jks'
|
||||
} >> gradle.properties
|
||||
echo "${{ secrets.KEYSTORE }}" | base64 -d > key.jks
|
||||
fi
|
||||
|
||||
- name: Download arm64 susfs
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: susfs-aarch64-linux-android
|
||||
path: .
|
||||
|
||||
- name: Download arm64 kpmmgr
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: kpmmgr-aarch64-linux-android
|
||||
path: .
|
||||
|
||||
- name: Download arm64 ksud
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud-aarch64-linux-android
|
||||
path: .
|
||||
|
||||
- name: Download x86_64 ksud
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud-x86_64-linux-android
|
||||
path: .
|
||||
|
||||
- name: Download arm ksud
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ksud-armv7-linux-androideabi
|
||||
path: .
|
||||
|
||||
- name: Copy ksud to app jniLibs
|
||||
run: |
|
||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||
mkdir -p app/src/main/jniLibs/x86_64
|
||||
mkdir -p app/src/main/jniLibs/armeabi-v7a
|
||||
cp -f ../aarch64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozako.so
|
||||
cp -f ../x86_64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/x86_64/libzakozako.so
|
||||
cp -f ../armv7-linux-androideabi/release/zakozako ../manager/app/src/main/jniLibs/armeabi-v7a/libzakozako.so
|
||||
|
||||
- name: Copy kpmmgr to app jniLibs
|
||||
run: |
|
||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||
cp -f ../arm64-v8a/kpmmgr ../manager/app/src/main/jniLibs/arm64-v8a/libkpmmgr.so
|
||||
|
||||
- name: Copy susfs to app jniLibs
|
||||
run: |
|
||||
mkdir -p app/src/main/jniLibs/arm64-v8a
|
||||
cp -f ../arm64-v8a/zakozakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozakozako.so
|
||||
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
export ANDROID_HOME=/root/.android/sdk
|
||||
export PATH=$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$PATH
|
||||
./gradlew clean assembleRelease
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
||||
with:
|
||||
name: manager
|
||||
path: manager/app/build/outputs/apk/release/*.apk
|
||||
|
||||
- name: Upload mappings
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
|
||||
with:
|
||||
name: "mappings"
|
||||
path: "manager/app/build/outputs/mapping/release/"
|
||||
|
||||
- name: Bot session cache
|
||||
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
|
||||
id: bot_session_cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: scripts/ksubot.session
|
||||
key: ${{ runner.os }}-bot-session
|
||||
|
||||
- name: Upload to telegram
|
||||
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
|
||||
env:
|
||||
CHAT_ID: ${{ vars.CHAT_ID }}
|
||||
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
||||
MESSAGE_THREAD_ID: ${{ vars.MESSAGE_THREAD_ID }}
|
||||
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
|
||||
COMMIT_URL: ${{ github.event.head_commit.url }}
|
||||
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
TITLE: Manager
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
||||
export VERSION=$(git rev-list --count HEAD)
|
||||
APK=$(find ./app/build/outputs/apk/release -name "*.apk")
|
||||
python3 $GITHUB_WORKSPACE/scripts/ksubot.py $APK
|
||||
fi
|
||||
252
.github/workflows/gki-kernel-local.yml
vendored
252
.github/workflows/gki-kernel-local.yml
vendored
@@ -1,252 +0,0 @@
|
||||
name: GKI Kernel Build Local
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: string
|
||||
description: >
|
||||
Output directory of gki,
|
||||
for example: android12-5.10
|
||||
version_name:
|
||||
required: true
|
||||
type: string
|
||||
description: >
|
||||
With SUBLEVEL of kernel,
|
||||
for example: android12-5.10.66
|
||||
tag:
|
||||
required: true
|
||||
type: string
|
||||
description: >
|
||||
Part of branch name of common kernel manifest,
|
||||
for example: android12-5.10-2021-11
|
||||
os_patch_level:
|
||||
required: false
|
||||
type: string
|
||||
description: >
|
||||
Patch level of common kernel manifest,
|
||||
for example: 2021-11
|
||||
default: 2022-05
|
||||
patch_path:
|
||||
required: false
|
||||
type: string
|
||||
description: >
|
||||
Directory name of .github/patches/<patch_path>
|
||||
for example: 5.10
|
||||
use_cache:
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
embed_ksud:
|
||||
required: false
|
||||
type: string
|
||||
default: ksud-aarch64-linux-android
|
||||
description: >
|
||||
Artifact name of prebuilt ksud to be embedded
|
||||
for example: ksud-aarch64-linux-android
|
||||
debug:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
build_lkm:
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
secrets:
|
||||
BOOT_SIGN_KEY:
|
||||
required: false
|
||||
CHAT_ID:
|
||||
required: false
|
||||
BOT_TOKEN:
|
||||
required: false
|
||||
MESSAGE_THREAD_ID:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build ${{ inputs.version_name }}
|
||||
runs-on: self-hosted
|
||||
env:
|
||||
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
|
||||
CCACHE_NOHASHDIR: "true"
|
||||
CCACHE_HARDLINK: "true"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
path: KernelSU
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup need_upload
|
||||
id: need_upload
|
||||
run: |
|
||||
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
|
||||
echo "UPLOAD=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "UPLOAD=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Setup kernel source
|
||||
run: |
|
||||
echo "Free space:"
|
||||
df -h
|
||||
cd $GITHUB_WORKSPACE
|
||||
sudo apt-get install repo -y
|
||||
export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo'
|
||||
mkdir android-kernel && cd android-kernel
|
||||
repo init --depth=1 --u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/kernel/manifest -b common-${{ inputs.tag }} --repo-rev=v2.35
|
||||
REMOTE_BRANCH=$(git ls-remote https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/kernel/common ${{ inputs.tag }})
|
||||
DEFAULT_MANIFEST_PATH=.repo/manifests/default.xml
|
||||
if grep -q deprecated <<< $REMOTE_BRANCH; then
|
||||
echo "Found deprecated branch: ${{ inputs.tag }}"
|
||||
sed -i 's/"${{ inputs.tag }}"/"deprecated\/${{ inputs.tag }}"/g' $DEFAULT_MANIFEST_PATH
|
||||
cat $DEFAULT_MANIFEST_PATH
|
||||
fi
|
||||
repo --version
|
||||
repo --trace sync -c -j$(nproc --all) --no-tags
|
||||
df -h
|
||||
|
||||
- name: Setup KernelSU
|
||||
env:
|
||||
PATCH_PATH: ${{ inputs.patch_path }}
|
||||
IS_DEBUG_KERNEL: ${{ inputs.debug }}
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/android-kernel
|
||||
echo "[+] KernelSU setup"
|
||||
GKI_ROOT=$(pwd)
|
||||
echo "[+] GKI_ROOT: $GKI_ROOT"
|
||||
echo "[+] Copy KernelSU driver to $GKI_ROOT/common/drivers"
|
||||
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $GKI_ROOT/common/drivers/kernelsu
|
||||
echo "[+] Add KernelSU driver to Makefile"
|
||||
DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile
|
||||
DRIVER_KCONFIG=$GKI_ROOT/common/drivers/Kconfig
|
||||
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE"
|
||||
grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG"
|
||||
echo "[+] Apply Compilation Patches"
|
||||
if [ ! -e build/build.sh ]; then
|
||||
GLIBC_VERSION=$(ldd --version 2>/dev/null | head -n 1 | awk '{print $NF}')
|
||||
echo "GLIBC_VERSION: $GLIBC_VERSION"
|
||||
if [ "$(printf '%s\n' "2.38" "$GLIBC_VERSION" | sort -V | head -n1)" = "2.38" ]; then
|
||||
echo "Patching resolve_btfids/Makefile"
|
||||
cd $GKI_ROOT/common/ && sed -i '/\$(Q)\$(MAKE) -C \$(SUBCMD_SRC) OUTPUT=\$(abspath \$(dir \$@))\/ \$(abspath \$@)/s//$(Q)$(MAKE) -C $(SUBCMD_SRC) EXTRA_CFLAGS="$(CFLAGS)" OUTPUT=$(abspath $(dir $@))\/ $(abspath $@)/' tools/bpf/resolve_btfids/Makefile || echo "No patch needed."
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$IS_DEBUG_KERNEL" = "true" ]; then
|
||||
echo "[+] Enable debug features for kernel"
|
||||
printf "\nccflags-y += -DCONFIG_KSU_DEBUG\n" >> $GITHUB_WORKSPACE/KernelSU/kernel/Makefile
|
||||
fi
|
||||
repo status
|
||||
echo "[+] KernelSU setup done."
|
||||
|
||||
- name: Symbol magic
|
||||
run: |
|
||||
echo "[+] Export all symbol from abi_gki_aarch64.xml"
|
||||
COMMON_ROOT=$GITHUB_WORKSPACE/android-kernel/common
|
||||
KSU_ROOT=$GITHUB_WORKSPACE/KernelSU
|
||||
ABI_XML=$COMMON_ROOT/android/abi_gki_aarch64.xml
|
||||
SYMBOL_LIST=$COMMON_ROOT/android/abi_gki_aarch64
|
||||
# python3 $KSU_ROOT/scripts/abi_gki_all.py $ABI_XML > $SYMBOL_LIST
|
||||
echo "[+] Add KernelSU symbols"
|
||||
cat $KSU_ROOT/kernel/export_symbol.txt | awk '{sub("[ \t]+","");print " "$0}' >> $SYMBOL_LIST
|
||||
|
||||
- name: Setup ccache
|
||||
if: inputs.use_cache == true
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: gki-kernel-aarch64-${{ inputs.version_name }}
|
||||
max-size: 2G
|
||||
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
||||
|
||||
- name: Setup for LKM
|
||||
if: ${{ inputs.build_lkm == true }}
|
||||
working-directory: android-kernel
|
||||
run: |
|
||||
pip install ast-grep-cli
|
||||
sudo apt-get install llvm-15 -y
|
||||
ast-grep -U -p '$$$ check_exports($$$) {$$$}' -r '' common/scripts/mod/modpost.c
|
||||
ast-grep -U -p 'check_exports($$$);' -r '' common/scripts/mod/modpost.c
|
||||
sed -i '/config KSU/,/help/{s/default y/default m/}' common/drivers/kernelsu/Kconfig
|
||||
echo "drivers/kernelsu/kernelsu.ko" >> common/android/gki_aarch64_modules
|
||||
|
||||
# bazel build, android14-5.15, android14-6.1 use bazel
|
||||
if [ ! -e build/build.sh ]; then
|
||||
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"
|
||||
if [ -e common/modules.bzl ]; then
|
||||
sed -i 's/_COMMON_GKI_MODULES_LIST = \[/_COMMON_GKI_MODULES_LIST = \[ "drivers\/kernelsu\/kernelsu.ko",/g' common/modules.bzl
|
||||
fi
|
||||
else
|
||||
TARGET_FILE="build/kernel/build.sh"
|
||||
if [ ! -e "$TARGET_FILE" ]; then
|
||||
TARGET_FILE="build/build.sh"
|
||||
fi
|
||||
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' $TARGET_FILE || echo "No unknown symbol in $TARGET_FILE"
|
||||
sed -i 's/if ! diff -u "\${KERNEL_DIR}\/\${MODULES_ORDER}" "\${OUT_DIR}\/modules\.order"; then/if false; then/g' $TARGET_FILE
|
||||
sed -i 's@${ROOT_DIR}/build/abi/compare_to_symbol_list@echo@g' $TARGET_FILE
|
||||
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"
|
||||
fi
|
||||
|
||||
- name: Make working directory clean to avoid dirty
|
||||
working-directory: android-kernel
|
||||
run: |
|
||||
if [ -e common/BUILD.bazel ]; then
|
||||
sed -i '/^[[:space:]]*"protected_exports_list"[[:space:]]*:[[:space:]]*"android\/abi_gki_protected_exports_aarch64",$/d' common/BUILD.bazel
|
||||
fi
|
||||
rm common/android/abi_gki_protected_exports_* || echo "No protected exports!"
|
||||
git config --global user.email "bot@kernelsu.org"
|
||||
git config --global user.name "KernelSUBot"
|
||||
cd common/ && git add -A && git commit -a -m "Add KernelSU"
|
||||
repo status
|
||||
|
||||
- name: Build Kernel/LKM
|
||||
working-directory: android-kernel
|
||||
run: |
|
||||
if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then
|
||||
export KSU_EXPECTED_SIZE=${{ vars.EXPECTED_SIZE }}
|
||||
export KSU_EXPECTED_HASH=${{ vars.EXPECTED_HASH }}
|
||||
fi
|
||||
if [ -e build/build.sh ]; then
|
||||
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh CC="/usr/bin/ccache clang"
|
||||
else
|
||||
tools/bazel run --disk_cache=/home/runner/.cache/bazel --config=fast --config=stamp --lto=thin //common:kernel_aarch64_dist -- --dist_dir=dist
|
||||
fi
|
||||
|
||||
- name: Prepare artifacts
|
||||
id: prepareArtifacts
|
||||
run: |
|
||||
OUTDIR=android-kernel/out/${{ inputs.version }}/dist
|
||||
if [ ! -e $OUTDIR ]; then
|
||||
OUTDIR=android-kernel/dist
|
||||
fi
|
||||
mkdir output
|
||||
if [ "${{ inputs.build_lkm}}" = "true" ]; then
|
||||
llvm-strip-15 -d $OUTDIR/kernelsu.ko
|
||||
mv $OUTDIR/kernelsu.ko ./output/${{ inputs.version }}_kernelsu.ko
|
||||
else
|
||||
cp $OUTDIR/Image ./output/
|
||||
cp $OUTDIR/Image.lz4 ./output/
|
||||
git clone https://github.com/Kernel-SU/AnyKernel3
|
||||
rm -rf ./AnyKernel3/.git
|
||||
cp $OUTDIR/Image ./AnyKernel3/
|
||||
fi
|
||||
|
||||
- name: Upload Image and Image.gz
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ inputs.build_lkm == false }}
|
||||
with:
|
||||
name: Image-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
|
||||
path: ./output/*
|
||||
|
||||
- name: Upload AnyKernel3
|
||||
if: ${{ inputs.build_lkm == false }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AnyKernel3-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
|
||||
path: ./AnyKernel3/*
|
||||
|
||||
- name: Upload LKM
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ inputs.build_lkm == true }}
|
||||
with:
|
||||
name: ${{ inputs.version }}-lkm
|
||||
path: ./output/*_kernelsu.ko
|
||||
@@ -7,6 +7,7 @@ kernelsu-objs += pkg_observer.o
|
||||
kernelsu-objs += throne_tracker.o
|
||||
kernelsu-objs += core_hook.o
|
||||
kernelsu-objs += supercalls.o
|
||||
kernelsu-objs += feature.o
|
||||
kernelsu-objs += ksud.o
|
||||
kernelsu-objs += embed_ksud.o
|
||||
kernelsu-objs += kernel_compat.o
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "allowlist.h"
|
||||
#include "arch.h"
|
||||
#include "core_hook.h"
|
||||
#include "feature.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "ksu.h"
|
||||
#include "ksud.h"
|
||||
@@ -78,6 +79,29 @@ struct ksu_umount_work {
|
||||
struct mnt_namespace *mnt_ns;
|
||||
};
|
||||
|
||||
static bool ksu_kernel_umount_enabled = true;
|
||||
|
||||
static int kernel_umount_feature_get(u64 *value)
|
||||
{
|
||||
*value = ksu_kernel_umount_enabled ? 1 : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kernel_umount_feature_set(u64 value)
|
||||
{
|
||||
bool enable = value != 0;
|
||||
ksu_kernel_umount_enabled = enable;
|
||||
pr_info("kernel_umount: set to %d\n", enable);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ksu_feature_handler kernel_umount_handler = {
|
||||
.feature_id = KSU_FEATURE_KERNEL_UMOUNT,
|
||||
.name = "kernel_umount",
|
||||
.get_handler = kernel_umount_feature_get,
|
||||
.set_handler = kernel_umount_feature_set,
|
||||
};
|
||||
|
||||
static inline bool is_allow_su()
|
||||
{
|
||||
if (is_manager()) {
|
||||
@@ -539,8 +563,8 @@ static void do_umount_work(struct work_struct *work)
|
||||
// try umount ksu temp path
|
||||
try_umount("/debug_ramdisk", false, MNT_DETACH);
|
||||
|
||||
// fixme: dec refcount
|
||||
current->nsproxy->mnt_ns = old_mnt_ns;
|
||||
put_mnt_ns(umount_work->mnt_ns);
|
||||
|
||||
kfree(umount_work);
|
||||
}
|
||||
@@ -588,6 +612,10 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ksu_kernel_umount_enabled) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ksu_uid_should_umount(new_uid.val)) {
|
||||
return 0;
|
||||
} else {
|
||||
@@ -622,8 +650,8 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// fixme: inc refcount
|
||||
umount_work->mnt_ns = current->nsproxy->mnt_ns;
|
||||
get_mnt_ns(umount_work->mnt_ns);
|
||||
|
||||
INIT_WORK(&umount_work->work, do_umount_work);
|
||||
|
||||
@@ -668,9 +696,8 @@ static struct kprobe reboot_kp = {
|
||||
// 2. security_task_fix_setuid hook for handling setuid
|
||||
static int security_task_fix_setuid_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||
struct cred *new = (struct cred *)PT_REGS_PARM1(real_regs);
|
||||
const struct cred *old = (const struct cred *)PT_REGS_PARM2(real_regs);
|
||||
struct cred *new = (struct cred *)PT_REGS_PARM1(regs);
|
||||
const struct cred *old = (const struct cred *)PT_REGS_PARM2(regs);
|
||||
|
||||
ksu_handle_setuid(new, old);
|
||||
|
||||
@@ -844,6 +871,10 @@ __maybe_unused int ksu_kprobe_exit(void)
|
||||
|
||||
void __init ksu_core_init(void)
|
||||
{
|
||||
if (ksu_register_feature_handler(&kernel_umount_handler)) {
|
||||
pr_err("Failed to register kernel_umount feature handler\n");
|
||||
}
|
||||
|
||||
ksu_workqueue = alloc_workqueue("ksu_umount", WQ_UNBOUND, 0);
|
||||
if (!ksu_workqueue) {
|
||||
pr_err("Failed to create ksu workqueue\n");
|
||||
|
||||
188
kernel/feature.c
Normal file
188
kernel/feature.c
Normal file
@@ -0,0 +1,188 @@
|
||||
#include "feature.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
static 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)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
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] = kmalloc(sizeof(struct ksu_feature_handler), GFP_KERNEL);
|
||||
if (!feature_handlers[handler->feature_id]) {
|
||||
pr_err("feature: failed to allocate handler for %u\n", handler->feature_id);
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(feature_handlers[handler->feature_id], handler, sizeof(struct ksu_feature_handler));
|
||||
|
||||
pr_info("feature: registered handler for %s (id=%u)\n",
|
||||
handler->name ? handler->name : "unknown", handler->feature_id);
|
||||
|
||||
out:
|
||||
mutex_unlock(&feature_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
kfree(feature_handlers[feature_id]);
|
||||
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;
|
||||
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;
|
||||
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("feature: feature management initialized\n");
|
||||
}
|
||||
|
||||
void ksu_feature_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
mutex_lock(&feature_mutex);
|
||||
|
||||
for (i = 0; i < KSU_FEATURE_MAX; i++) {
|
||||
if (feature_handlers[i]) {
|
||||
kfree(feature_handlers[i]);
|
||||
feature_handlers[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&feature_mutex);
|
||||
|
||||
pr_info("feature: feature management cleaned up\n");
|
||||
}
|
||||
35
kernel/feature.h
Normal file
35
kernel/feature.h
Normal 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
|
||||
@@ -8,6 +8,7 @@
|
||||
#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"
|
||||
@@ -26,6 +27,7 @@ extern void ksu_sucompat_init();
|
||||
extern void ksu_sucompat_exit();
|
||||
extern void ksu_ksud_init();
|
||||
extern void ksu_ksud_exit();
|
||||
extern void ksu_supercalls_init();
|
||||
#ifdef CONFIG_KSU_TRACEPOINT_HOOK
|
||||
extern void ksu_trace_register();
|
||||
extern void ksu_trace_unregister();
|
||||
@@ -43,6 +45,10 @@ int __init kernelsu_init(void)
|
||||
pr_alert("*************************************************************");
|
||||
#endif
|
||||
|
||||
ksu_feature_init();
|
||||
|
||||
ksu_supercalls_init();
|
||||
|
||||
ksu_core_init();
|
||||
|
||||
ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
|
||||
@@ -89,6 +95,7 @@ void kernelsu_exit(void)
|
||||
#endif
|
||||
|
||||
ksu_core_exit();
|
||||
ksu_feature_exit();
|
||||
}
|
||||
|
||||
module_init(kernelsu_init);
|
||||
|
||||
@@ -13,6 +13,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"
|
||||
@@ -22,6 +23,44 @@
|
||||
#define SH_PATH "/system/bin/sh"
|
||||
|
||||
extern void escape_to_root();
|
||||
void ksu_sucompat_enable();
|
||||
void ksu_sucompat_disable();
|
||||
|
||||
bool ksu_su_compat_enabled = 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,
|
||||
};
|
||||
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
static bool ksu_sucompat_hook_state __read_mostly = true;
|
||||
@@ -340,7 +379,7 @@ static void destroy_kprobe(struct kprobe **kp_ptr)
|
||||
#endif
|
||||
|
||||
// sucompat: permited process can execute 'su' to gain root access.
|
||||
void ksu_sucompat_init()
|
||||
void ksu_sucompat_enable()
|
||||
{
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre);
|
||||
@@ -353,7 +392,7 @@ void ksu_sucompat_init()
|
||||
#endif
|
||||
}
|
||||
|
||||
void ksu_sucompat_exit()
|
||||
void ksu_sucompat_disable()
|
||||
{
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
int i;
|
||||
@@ -365,3 +404,23 @@ void ksu_sucompat_exit()
|
||||
pr_info("ksu_sucompat_exit: hooks disabled: execve/execveat_su, faccessat, stat\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
// sucompat: permited process can execute 'su' to gain root access.
|
||||
void ksu_sucompat_init()
|
||||
{
|
||||
if (ksu_register_feature_handler(&su_compat_handler)) {
|
||||
pr_err("Failed to register su_compat feature handler\n");
|
||||
}
|
||||
if (ksu_su_compat_enabled) {
|
||||
ksu_sucompat_enable();
|
||||
}
|
||||
}
|
||||
|
||||
void ksu_sucompat_exit()
|
||||
{
|
||||
if (ksu_su_compat_enabled) {
|
||||
ksu_sucompat_disable();
|
||||
}
|
||||
ksu_unregister_feature_handler(KSU_FEATURE_SU_COMPAT);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <linux/version.h>
|
||||
|
||||
#include "allowlist.h"
|
||||
#include "feature.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "ksud.h"
|
||||
#include "manager.h"
|
||||
@@ -32,29 +33,25 @@ extern int handle_sepolicy(unsigned long arg3, void __user *arg4);
|
||||
extern void ksu_sucompat_init(void);
|
||||
extern void ksu_sucompat_exit(void);
|
||||
|
||||
// Forward declaration for anon_ksu_fops
|
||||
static const struct file_operations anon_ksu_fops;
|
||||
|
||||
static bool ksu_su_compat_enabled = true;
|
||||
bool ksu_uid_scanner_enabled = false;
|
||||
|
||||
// Permission check functions
|
||||
bool perm_check_manager(void)
|
||||
bool only_manager(void)
|
||||
{
|
||||
return is_manager();
|
||||
}
|
||||
|
||||
bool perm_check_root(void)
|
||||
bool only_root(void)
|
||||
{
|
||||
return current_uid().val == 0;
|
||||
}
|
||||
|
||||
bool perm_check_basic(void)
|
||||
bool manager_or_root(void)
|
||||
{
|
||||
return current_uid().val == 0 || is_manager();
|
||||
}
|
||||
|
||||
bool perm_check_all(void)
|
||||
bool always_allow(void)
|
||||
{
|
||||
return true; // No permission check
|
||||
}
|
||||
@@ -101,6 +98,7 @@ static int do_get_info(void __user *arg)
|
||||
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");
|
||||
@@ -318,41 +316,48 @@ static int do_set_app_profile(void __user *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_is_su_enabled(void __user *arg)
|
||||
static int do_get_feature(void __user *arg)
|
||||
{
|
||||
struct ksu_is_su_enabled_cmd cmd;
|
||||
|
||||
cmd.enabled = ksu_su_compat_enabled;
|
||||
|
||||
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||
pr_err("is_su_enabled: copy_to_user failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_enable_su(void __user *arg)
|
||||
{
|
||||
struct ksu_enable_su_cmd cmd;
|
||||
struct ksu_get_feature_cmd cmd;
|
||||
bool supported;
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||
pr_err("enable_su: copy_from_user failed\n");
|
||||
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;
|
||||
}
|
||||
|
||||
if (cmd.enable == ksu_su_compat_enabled) {
|
||||
pr_info("enable_su: no need to change\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cmd.enable) {
|
||||
ksu_sucompat_init();
|
||||
} else {
|
||||
ksu_sucompat_exit();
|
||||
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;
|
||||
}
|
||||
|
||||
ksu_su_compat_enabled = cmd.enable;
|
||||
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;
|
||||
}
|
||||
@@ -517,81 +522,80 @@ static int do_enable_uid_scanner(void __user *arg)
|
||||
|
||||
// IOCTL handlers mapping table
|
||||
static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = {
|
||||
{ .cmd = KSU_IOCTL_GRANT_ROOT, .handler = do_grant_root, .perm_check = perm_check_basic, .name = "do_grant_root"},
|
||||
{ .cmd = KSU_IOCTL_GET_INFO, .handler = do_get_info, .perm_check = perm_check_all, .name = "do_get_info"},
|
||||
{ .cmd = KSU_IOCTL_REPORT_EVENT, .handler = do_report_event, .perm_check = perm_check_root, .name = "do_report_event"},
|
||||
{ .cmd = KSU_IOCTL_SET_SEPOLICY, .handler = do_set_sepolicy, .perm_check = perm_check_root, .name = "do_set_sepolicy"},
|
||||
{ .cmd = KSU_IOCTL_CHECK_SAFEMODE, .handler = do_check_safemode, .perm_check = perm_check_all, .name = "do_check_safemode"},
|
||||
{ .cmd = KSU_IOCTL_GET_ALLOW_LIST, .handler = do_get_allow_list, .perm_check = perm_check_basic, .name = "do_get_allow_list"},
|
||||
{ .cmd = KSU_IOCTL_GET_DENY_LIST, .handler = do_get_deny_list, .perm_check = perm_check_basic, .name = "do_get_deny_list"},
|
||||
{ .cmd = KSU_IOCTL_UID_GRANTED_ROOT, .handler = do_uid_granted_root, .perm_check = perm_check_basic, .name = "do_uid_granted_root"},
|
||||
{ .cmd = KSU_IOCTL_UID_SHOULD_UMOUNT, .handler = do_uid_should_umount, .perm_check = perm_check_basic, .name = "do_uid_should_umount"},
|
||||
{ .cmd = KSU_IOCTL_GET_MANAGER_UID, .handler = do_get_manager_uid, .perm_check = perm_check_basic, .name = "do_get_manager_uid"},
|
||||
{ .cmd = KSU_IOCTL_GET_APP_PROFILE, .handler = do_get_app_profile, .perm_check = perm_check_manager, .name = "do_get_app_profile"},
|
||||
{ .cmd = KSU_IOCTL_SET_APP_PROFILE, .handler = do_set_app_profile, .perm_check = perm_check_manager, .name = "do_set_app_profile"},
|
||||
{ .cmd = KSU_IOCTL_IS_SU_ENABLED, .handler = do_is_su_enabled, .perm_check = perm_check_manager, .name = "do_is_su_enabled"},
|
||||
{ .cmd = KSU_IOCTL_ENABLE_SU, .handler = do_enable_su, .perm_check = perm_check_manager, .name = "do_enable_su"},
|
||||
{ .cmd = KSU_IOCTL_GET_FULL_VERSION, .handler = do_get_full_version, .perm_check = perm_check_manager, .name = "do_get_full_version"},
|
||||
{ .cmd = KSU_IOCTL_HOOK_TYPE, .handler = do_get_hook_type, .perm_check = perm_check_basic, .name = "do_get_hook_type"},
|
||||
{ .cmd = KSU_IOCTL_ENABLE_KPM, .handler = do_enable_kpm, .perm_check = perm_check_basic, .name = "do_enable_kpm"},
|
||||
{ .cmd = KSU_IOCTL_DYNAMIC_MANAGER, .handler = do_dynamic_manager, .perm_check = perm_check_basic, .name = "do_dynamic_manager"},
|
||||
{ .cmd = KSU_IOCTL_GET_MANAGERS, .handler = do_get_managers, .perm_check = perm_check_basic, .name = "do_get_managers"},
|
||||
{ .cmd = KSU_IOCTL_ENABLE_UID_SCANNER, .handler = do_enable_uid_scanner, .perm_check = perm_check_basic, .name = "do_enable_uid_scanner"},
|
||||
{ .cmd = 0, .handler = NULL, .perm_check = NULL, .name = NULL} // Sentinel
|
||||
{ .cmd = KSU_IOCTL_GRANT_ROOT, .name = "GRANT_ROOT", .handler = do_grant_root, .perm_check = manager_or_root },
|
||||
{ .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},
|
||||
{ .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;
|
||||
const char *cmd_name = "unknown";
|
||||
int ret = -ENOTTY;
|
||||
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_info("ksu ioctl: cmd=0x%x from uid=%d\n", cmd, current_uid().val);
|
||||
#endif
|
||||
|
||||
// Determine the command name based on the cmd value
|
||||
for (i = 0; ksu_ioctl_handlers[i].handler; i++) {
|
||||
if (cmd == ksu_ioctl_handlers[i].cmd) {
|
||||
cmd_name = ksu_ioctl_handlers[i].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
#if __SULOG_GATE
|
||||
ksu_sulog_report_syscall(current_uid().val, NULL, cmd_name, "DENIED");
|
||||
#endif
|
||||
ksu_ioctl_audit(cmd, ksu_ioctl_handlers[i].name,
|
||||
current_uid().val, -EPERM);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
// Execute handler
|
||||
ret = ksu_ioctl_handlers[i].handler(argp);
|
||||
|
||||
// Log the result of the ioctl command
|
||||
if (ret == 0) {
|
||||
#if __SULOG_GATE
|
||||
ksu_sulog_report_syscall(current_uid().val, NULL, cmd_name, "SUCCESS");
|
||||
#endif
|
||||
} else {
|
||||
#if __SULOG_GATE
|
||||
ksu_sulog_report_syscall(current_uid().val, NULL, cmd_name, "FAILED");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ksu_ioctl_handlers[i].handler == NULL) {
|
||||
pr_warn("ksu ioctl: unsupported command 0x%x\n", cmd);
|
||||
ret = -ENOTTY;
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@@ -18,6 +18,7 @@ struct ksu_become_daemon_cmd {
|
||||
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 {
|
||||
@@ -61,12 +62,15 @@ struct ksu_set_app_profile_cmd {
|
||||
struct app_profile profile; // Input: app profile structure
|
||||
};
|
||||
|
||||
struct ksu_is_su_enabled_cmd {
|
||||
__u8 enabled; // Output: true if su compat enabled
|
||||
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_enable_su_cmd {
|
||||
__u8 enable; // Input: true to enable, false to disable
|
||||
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
|
||||
@@ -109,8 +113,8 @@ struct ksu_enable_uid_scanner_cmd {
|
||||
#define KSU_IOCTL_GET_MANAGER_UID _IOR('K', 10, struct ksu_get_manager_uid_cmd)
|
||||
#define KSU_IOCTL_GET_APP_PROFILE _IOWR('K', 11, struct ksu_get_app_profile_cmd)
|
||||
#define KSU_IOCTL_SET_APP_PROFILE _IOW('K', 12, struct ksu_set_app_profile_cmd)
|
||||
#define KSU_IOCTL_IS_SU_ENABLED _IOR('K', 13, struct ksu_is_su_enabled_cmd)
|
||||
#define KSU_IOCTL_ENABLE_SU _IOW('K', 14, struct ksu_enable_su_cmd)
|
||||
#define KSU_IOCTL_GET_FEATURE _IOWR('K', 13, struct ksu_get_feature_cmd)
|
||||
#define KSU_IOCTL_SET_FEATURE _IOW('K', 14, struct ksu_set_feature_cmd)
|
||||
// Other IOCTL command definitions
|
||||
#define KSU_IOCTL_GET_FULL_VERSION _IOR('K', 100, struct ksu_get_full_version_cmd)
|
||||
#define KSU_IOCTL_HOOK_TYPE _IOR('K', 101, struct ksu_hook_type_cmd)
|
||||
@@ -123,18 +127,12 @@ struct ksu_enable_uid_scanner_cmd {
|
||||
typedef int (*ksu_ioctl_handler_t)(void __user *arg);
|
||||
typedef bool (*ksu_perm_check_t)(void);
|
||||
|
||||
// Permission check functions
|
||||
bool perm_check_manager(void);
|
||||
bool perm_check_root(void);
|
||||
bool perm_check_basic(void);
|
||||
bool perm_check_all(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
|
||||
const char *name; // Command name for logging
|
||||
};
|
||||
|
||||
// Install KSU fd to current process
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <sys/prctl.h>
|
||||
#include <android/log.h>
|
||||
#include <string.h>
|
||||
#include <linux/capability.h>
|
||||
|
||||
NativeBridgeNP(getVersion, jint) {
|
||||
uint32_t version = get_version();
|
||||
@@ -300,6 +301,14 @@ NativeBridge(setSuEnabled, jboolean, jboolean enabled) {
|
||||
return set_su_enabled(enabled);
|
||||
}
|
||||
|
||||
NativeBridgeNP(isKernelUmountEnabled, jboolean) {
|
||||
return is_kernel_umount_enabled();
|
||||
}
|
||||
|
||||
NativeBridge(setKernelUmountEnabled, jboolean, jboolean enabled) {
|
||||
return set_kernel_umount_enabled(enabled);
|
||||
}
|
||||
|
||||
// Check if KPM is enabled
|
||||
NativeBridgeNP(isKPMEnabled, jboolean) {
|
||||
return is_KPM_enable();
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <android/log.h>
|
||||
#include <dirent.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <sys/syscall.h>
|
||||
|
||||
@@ -133,13 +134,56 @@ int get_app_profile(struct app_profile *profile) {
|
||||
}
|
||||
|
||||
bool set_su_enabled(bool enabled) {
|
||||
struct ksu_enable_su_cmd cmd = {.enable = enabled};
|
||||
return ksuctl(KSU_IOCTL_ENABLE_SU, &cmd) == 0;
|
||||
struct ksu_set_feature_cmd cmd = {};
|
||||
cmd.feature_id = KSU_FEATURE_SU_COMPAT;
|
||||
cmd.value = enabled ? 1 : 0;
|
||||
return ksuctl(KSU_IOCTL_SET_FEATURE, &cmd) == 0;
|
||||
}
|
||||
|
||||
bool is_su_enabled() {
|
||||
struct ksu_is_su_enabled_cmd cmd = {};
|
||||
return ksuctl(KSU_IOCTL_IS_SU_ENABLED, &cmd) == 0 && cmd.enabled;
|
||||
struct ksu_get_feature_cmd cmd = {};
|
||||
cmd.feature_id = KSU_FEATURE_SU_COMPAT;
|
||||
if (ksuctl(KSU_IOCTL_GET_FEATURE, &cmd) != 0) {
|
||||
return false;
|
||||
}
|
||||
if (!cmd.supported) {
|
||||
return false;
|
||||
}
|
||||
return cmd.value != 0;
|
||||
}
|
||||
|
||||
static inline bool get_feature(uint32_t feature_id, uint64_t *out_value, bool *out_supported) {
|
||||
struct ksu_get_feature_cmd cmd = {};
|
||||
cmd.feature_id = feature_id;
|
||||
if (ksuctl(KSU_IOCTL_GET_FEATURE, &cmd) != 0) {
|
||||
return false;
|
||||
}
|
||||
if (out_value) *out_value = cmd.value;
|
||||
if (out_supported) *out_supported = cmd.supported;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool set_feature(uint32_t feature_id, uint64_t value) {
|
||||
struct ksu_set_feature_cmd cmd = {};
|
||||
cmd.feature_id = feature_id;
|
||||
cmd.value = value;
|
||||
return ksuctl(KSU_IOCTL_SET_FEATURE, &cmd) == 0;
|
||||
}
|
||||
|
||||
bool set_kernel_umount_enabled(bool enabled) {
|
||||
return set_feature(KSU_FEATURE_KERNEL_UMOUNT, enabled ? 1 : 0);
|
||||
}
|
||||
|
||||
bool is_kernel_umount_enabled() {
|
||||
uint64_t value = 0;
|
||||
bool supported = false;
|
||||
if (!get_feature(KSU_FEATURE_KERNEL_UMOUNT, &value, &supported)) {
|
||||
return false;
|
||||
}
|
||||
if (!supported) {
|
||||
return false;
|
||||
}
|
||||
return value != 0;
|
||||
}
|
||||
|
||||
void get_full_version(char* buff) {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#define KERNELSU_KSU_H
|
||||
|
||||
#include "prelude.h"
|
||||
#include <linux/capability.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
@@ -105,10 +105,6 @@ bool set_app_profile(const struct app_profile* profile);
|
||||
|
||||
int get_app_profile(struct app_profile* profile);
|
||||
|
||||
bool set_su_enabled(bool enabled);
|
||||
|
||||
bool is_su_enabled();
|
||||
|
||||
bool is_KPM_enable();
|
||||
|
||||
void get_hook_type(char* hook_type);
|
||||
@@ -129,50 +125,69 @@ bool set_uid_scanner_enabled(bool enabled);
|
||||
|
||||
bool clear_uid_scanner_environment();
|
||||
|
||||
// Feature IDs
|
||||
enum ksu_feature_id {
|
||||
KSU_FEATURE_SU_COMPAT = 0,
|
||||
KSU_FEATURE_KERNEL_UMOUNT = 1,
|
||||
};
|
||||
|
||||
// Generic feature API
|
||||
struct ksu_get_feature_cmd {
|
||||
uint32_t feature_id; // Input: feature ID
|
||||
uint64_t value; // Output: feature value/state
|
||||
uint8_t supported; // Output: whether the feature is supported
|
||||
};
|
||||
|
||||
struct ksu_set_feature_cmd {
|
||||
uint32_t feature_id; // Input: feature ID
|
||||
uint64_t value; // Input: feature value/state to set
|
||||
};
|
||||
|
||||
struct ksu_become_daemon_cmd {
|
||||
__u8 token[65]; // Input: daemon token (null-terminated)
|
||||
uint8_t 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)
|
||||
uint32_t version; // Output: KERNEL_SU_VERSION
|
||||
uint32_t flags; // Output: flags (bit 0: MODULE mode)
|
||||
uint32_t features; // Output: max feature ID supported (KSU_FEATURE_MAX)
|
||||
};
|
||||
|
||||
struct ksu_report_event_cmd {
|
||||
__u32 event; // Input: EVENT_POST_FS_DATA, EVENT_BOOT_COMPLETED, etc.
|
||||
uint32_t 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
|
||||
uint64_t cmd; // Input: sepolicy command
|
||||
uint64_t arg; // Input: sepolicy argument pointer
|
||||
};
|
||||
|
||||
struct ksu_check_safemode_cmd {
|
||||
__u8 in_safe_mode; // Output: true if in safe mode, false otherwise
|
||||
uint8_t 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
|
||||
uint32_t uids[128]; // Output: array of allowed/denied UIDs
|
||||
uint32_t count; // Output: number of UIDs in array
|
||||
uint8_t 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
|
||||
uint32_t uid; // Input: target UID to check
|
||||
uint8_t 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
|
||||
uint32_t uid; // Input: target UID to check
|
||||
uint8_t should_umount; // Output: true if should umount, false otherwise
|
||||
};
|
||||
|
||||
struct ksu_get_manager_uid_cmd {
|
||||
__u32 uid; // Output: manager UID
|
||||
uint32_t uid; // Output: manager UID
|
||||
};
|
||||
|
||||
struct ksu_set_manager_uid_cmd {
|
||||
__u32 uid; // Input: new manager UID
|
||||
uint32_t uid; // Input: new manager UID
|
||||
};
|
||||
|
||||
struct ksu_get_app_profile_cmd {
|
||||
@@ -183,13 +198,13 @@ struct ksu_set_app_profile_cmd {
|
||||
struct app_profile profile; // Input: app profile structure
|
||||
};
|
||||
|
||||
struct ksu_is_su_enabled_cmd {
|
||||
__u8 enabled; // Output: true if su compat enabled
|
||||
};
|
||||
// Su compat
|
||||
bool set_su_enabled(bool enabled);
|
||||
bool is_su_enabled();
|
||||
|
||||
struct ksu_enable_su_cmd {
|
||||
__u8 enable; // Input: true to enable, false to disable
|
||||
};
|
||||
// Kernel umount
|
||||
bool set_kernel_umount_enabled(bool enabled);
|
||||
bool is_kernel_umount_enabled();
|
||||
|
||||
// Other command structures
|
||||
struct ksu_get_full_version_cmd {
|
||||
@@ -201,7 +216,7 @@ struct ksu_hook_type_cmd {
|
||||
};
|
||||
|
||||
struct ksu_enable_kpm_cmd {
|
||||
__u8 enabled; // Output: true if KPM is enabled
|
||||
uint8_t enabled; // Output: true if KPM is enabled
|
||||
};
|
||||
|
||||
struct ksu_dynamic_manager_cmd {
|
||||
@@ -213,9 +228,9 @@ struct ksu_get_managers_cmd {
|
||||
};
|
||||
|
||||
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)
|
||||
__u64 status_ptr; // Input: pointer to store status (for UID_SCANNER_OP_GET_STATUS)
|
||||
uint32_t operation; // Input: operation type (UID_SCANNER_OP_GET_STATUS, UID_SCANNER_OP_TOGGLE, UID_SCANNER_OP_CLEAR_ENV)
|
||||
uint32_t enabled; // Input: enable or disable (for UID_SCANNER_OP_TOGGLE)
|
||||
uint64_t status_ptr; // Input: pointer to store status (for UID_SCANNER_OP_GET_STATUS)
|
||||
};
|
||||
|
||||
// IOCTL command definitions
|
||||
@@ -231,8 +246,8 @@ struct ksu_enable_uid_scanner_cmd {
|
||||
#define KSU_IOCTL_GET_MANAGER_UID _IOR('K', 10, struct ksu_get_manager_uid_cmd)
|
||||
#define KSU_IOCTL_GET_APP_PROFILE _IOWR('K', 11, struct ksu_get_app_profile_cmd)
|
||||
#define KSU_IOCTL_SET_APP_PROFILE _IOW('K', 12, struct ksu_set_app_profile_cmd)
|
||||
#define KSU_IOCTL_IS_SU_ENABLED _IOR('K', 13, struct ksu_is_su_enabled_cmd)
|
||||
#define KSU_IOCTL_ENABLE_SU _IOW('K', 14, struct ksu_enable_su_cmd)
|
||||
#define KSU_IOCTL_GET_FEATURE _IOWR('K', 13, struct ksu_get_feature_cmd)
|
||||
#define KSU_IOCTL_SET_FEATURE _IOW('K', 14, struct ksu_set_feature_cmd)
|
||||
|
||||
// Other IOCTL command definitions
|
||||
#define KSU_IOCTL_GET_FULL_VERSION _IOR('K', 100, struct ksu_get_full_version_cmd)
|
||||
|
||||
@@ -98,7 +98,16 @@ object Natives {
|
||||
*/
|
||||
external fun isSuEnabled(): Boolean
|
||||
external fun setSuEnabled(enabled: Boolean): Boolean
|
||||
external fun grantRoot(): Boolean
|
||||
|
||||
/**
|
||||
* Kernel module umount can be disabled temporarily.
|
||||
* 0: disabled
|
||||
* 1: enabled
|
||||
* negative : error
|
||||
*/
|
||||
external fun isKernelUmountEnabled(): Boolean
|
||||
external fun setKernelUmountEnabled(enabled: Boolean): Boolean
|
||||
|
||||
external fun isKPMEnabled(): Boolean
|
||||
external fun getHookType(): String
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import androidx.compose.foundation.selection.selectable
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.Input
|
||||
import androidx.compose.material.icons.filled.AutoFixHigh
|
||||
import androidx.compose.material.icons.filled.FileUpload
|
||||
import androidx.compose.material.icons.filled.Security
|
||||
@@ -205,12 +206,29 @@ fun InstallScreen(
|
||||
}
|
||||
}
|
||||
|
||||
val selectLkmLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.StartActivityForResult()
|
||||
) {
|
||||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
it.data?.data?.let { uri ->
|
||||
lkmSelection = LkmSelection.LkmUri(uri)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val onLkmUpload = {
|
||||
selectLkmLauncher.launch(Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||
type = "application/octet-stream"
|
||||
})
|
||||
}
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopBar(
|
||||
onBack = { navigator.popBackStack() },
|
||||
onLkmUpload = onLkmUpload,
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
},
|
||||
@@ -934,6 +952,7 @@ fun rememberSelectKmiDialog(onSelected: (String?) -> Unit): DialogHandle {
|
||||
@Composable
|
||||
private fun TopBar(
|
||||
onBack: () -> Unit = {},
|
||||
onLkmUpload: () -> Unit = {},
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||
) {
|
||||
val colorScheme = MaterialTheme.colorScheme
|
||||
@@ -966,6 +985,17 @@ private fun TopBar(
|
||||
windowInsets = WindowInsets.safeDrawing.only(
|
||||
WindowInsetsSides.Top + WindowInsetsSides.Horizontal
|
||||
),
|
||||
actions = {
|
||||
IconButton(
|
||||
modifier = Modifier.padding(end = 16.dp),
|
||||
onClick = onLkmUpload
|
||||
) {
|
||||
Icon(
|
||||
Icons.AutoMirrored.Filled.Input,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
},
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.Undo
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material.icons.rounded.FolderDelete
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
@@ -165,6 +166,23 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
var isKernelUmountDisabled by rememberSaveable {
|
||||
mutableStateOf(!Natives.isKernelUmountEnabled())
|
||||
}
|
||||
SwitchItem(
|
||||
icon = Icons.Rounded.FolderDelete,
|
||||
title = stringResource(id = R.string.settings_disable_kernel_umount),
|
||||
summary = stringResource(id = R.string.settings_disable_kernel_umount_summary),
|
||||
checked = isKernelUmountDisabled,
|
||||
onCheckedChange = { checked: Boolean ->
|
||||
val shouldEnable = !checked
|
||||
if (Natives.setKernelUmountEnabled(shouldEnable)) {
|
||||
isKernelUmountDisabled = !shouldEnable
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// 强制签名验证开关
|
||||
var forceSignatureVerification by rememberSaveable {
|
||||
mutableStateOf(prefs.getBoolean("force_signature_verification", false))
|
||||
|
||||
@@ -164,6 +164,8 @@
|
||||
<string name="su_not_allowed">不允许授予 %s 超级用户权限</string>
|
||||
<string name="settings_disable_su">禁用 su 兼容性</string>
|
||||
<string name="settings_disable_su_summary">临时禁止任何应用程序通过 su 命令获取 Root 权限(现有的 Root 进程不受影响)</string>
|
||||
<string name="settings_disable_kernel_umount">关闭内核 umount</string>
|
||||
<string name="settings_disable_kernel_umount_summary">临时关闭 KernelSU 控制的内核级 umount 行为。</string>
|
||||
<string name="module_install_multiple_confirm_with_names">确定要安装以下 %1$d 个模块吗?\n\n%2$s</string>
|
||||
<string name="more_settings">更多设置</string>
|
||||
<string name="selinux">SELinux</string>
|
||||
|
||||
@@ -166,6 +166,8 @@
|
||||
<string name="su_not_allowed">Granting superuser to %s is not allowed</string>
|
||||
<string name="settings_disable_su">Disable su compatibility</string>
|
||||
<string name="settings_disable_su_summary">Temporarily disable any applications from obtaining root privileges via the su command (Existing root processes will not be affected)</string>
|
||||
<string name="settings_disable_kernel_umount">Disable kernel umount</string>
|
||||
<string name="settings_disable_kernel_umount_summary">Temporarily disable kernel-level umount behavior controlled by KernelSU.</string>
|
||||
<string name="module_install_multiple_confirm_with_names">Sure you want to install the following %1$d modules? \n\n%2$s</string>
|
||||
<string name="more_settings">More settings</string>
|
||||
<string name="selinux">SELinux</string>
|
||||
|
||||
@@ -64,6 +64,12 @@ enum Commands {
|
||||
command: Profile,
|
||||
},
|
||||
|
||||
/// Manage kernel features
|
||||
Feature {
|
||||
#[command(subcommand)]
|
||||
command: Feature,
|
||||
},
|
||||
|
||||
/// Patch boot or init_boot images to apply KernelSU
|
||||
BootPatch {
|
||||
/// boot image path, if not specified, will try to find the boot image automatically
|
||||
@@ -281,6 +287,32 @@ enum Profile {
|
||||
ListTemplates,
|
||||
}
|
||||
|
||||
#[derive(clap::Subcommand, Debug)]
|
||||
enum Feature {
|
||||
/// Get feature value and support status
|
||||
Get {
|
||||
/// Feature ID or name (su_compat, kernel_umount)
|
||||
id: String,
|
||||
},
|
||||
|
||||
/// Set feature value
|
||||
Set {
|
||||
/// Feature ID or name
|
||||
id: String,
|
||||
/// Feature value (0=disable, 1=enable)
|
||||
value: u64,
|
||||
},
|
||||
|
||||
/// List all available features
|
||||
List,
|
||||
|
||||
/// Load configuration from file and apply to kernel
|
||||
Load,
|
||||
|
||||
/// Save current kernel feature states to file
|
||||
Save,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
mod kpm_cmd {
|
||||
use clap::Subcommand;
|
||||
@@ -368,6 +400,14 @@ pub fn run() -> Result<()> {
|
||||
Profile::ListTemplates => crate::profile::list_templates(),
|
||||
},
|
||||
|
||||
Commands::Feature { command } => match command {
|
||||
Feature::Get { id } => crate::feature::get_feature(id),
|
||||
Feature::Set { id, value } => crate::feature::set_feature(id, value),
|
||||
Feature::List => crate::feature::list_features(),
|
||||
Feature::Load => crate::feature::load_config_and_apply(),
|
||||
Feature::Save => crate::feature::save_config(),
|
||||
},
|
||||
|
||||
Commands::Debug { command } => match command {
|
||||
Debug::SetManager { apk } => debug::set_manager(&apk),
|
||||
Debug::GetSign { apk } => {
|
||||
|
||||
282
userspace/ksud/src/feature.rs
Normal file
282
userspace/ksud/src/feature.rs
Normal file
@@ -0,0 +1,282 @@
|
||||
use anyhow::{Context, Result, bail};
|
||||
use const_format::concatcp;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::Path;
|
||||
|
||||
use crate::defs;
|
||||
|
||||
const FEATURE_CONFIG_PATH: &str = concatcp!(defs::WORKING_DIR, ".feature_config");
|
||||
const FEATURE_MAGIC: u32 = 0x7f4b5355;
|
||||
const FEATURE_VERSION: u32 = 1;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum FeatureId {
|
||||
SuCompat = 0,
|
||||
KernelUmount = 1,
|
||||
}
|
||||
|
||||
impl FeatureId {
|
||||
pub fn from_u32(id: u32) -> Option<Self> {
|
||||
match id {
|
||||
0 => Some(FeatureId::SuCompat),
|
||||
1 => Some(FeatureId::KernelUmount),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
FeatureId::SuCompat => "su_compat",
|
||||
FeatureId::KernelUmount => "kernel_umount",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn description(&self) -> &'static str {
|
||||
match self {
|
||||
FeatureId::SuCompat => {
|
||||
"SU Compatibility Mode - allows authorized apps to gain root via traditional 'su' command"
|
||||
}
|
||||
FeatureId::KernelUmount => {
|
||||
"Kernel Umount - controls whether kernel automatically unmounts modules when not needed"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_feature_id(name: &str) -> Result<FeatureId> {
|
||||
match name {
|
||||
"su_compat" | "0" => Ok(FeatureId::SuCompat),
|
||||
"kernel_umount" | "1" => Ok(FeatureId::KernelUmount),
|
||||
_ => bail!("Unknown feature: {}", name),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_binary_config() -> Result<HashMap<u32, u64>> {
|
||||
let path = Path::new(FEATURE_CONFIG_PATH);
|
||||
if !path.exists() {
|
||||
log::info!("Feature config not found, using defaults");
|
||||
return Ok(HashMap::new());
|
||||
}
|
||||
|
||||
let mut file = File::open(path).with_context(|| "Failed to open feature config")?;
|
||||
|
||||
let mut magic_buf = [0u8; 4];
|
||||
file.read_exact(&mut magic_buf)
|
||||
.with_context(|| "Failed to read magic")?;
|
||||
let magic = u32::from_le_bytes(magic_buf);
|
||||
|
||||
if magic != FEATURE_MAGIC {
|
||||
bail!(
|
||||
"Invalid feature config magic: expected 0x{:08x}, got 0x{:08x}",
|
||||
FEATURE_MAGIC,
|
||||
magic
|
||||
);
|
||||
}
|
||||
|
||||
let mut version_buf = [0u8; 4];
|
||||
file.read_exact(&mut version_buf)
|
||||
.with_context(|| "Failed to read version")?;
|
||||
let version = u32::from_le_bytes(version_buf);
|
||||
|
||||
if version != FEATURE_VERSION {
|
||||
log::warn!(
|
||||
"Feature config version mismatch: expected {}, got {}",
|
||||
FEATURE_VERSION,
|
||||
version
|
||||
);
|
||||
}
|
||||
|
||||
let mut count_buf = [0u8; 4];
|
||||
file.read_exact(&mut count_buf)
|
||||
.with_context(|| "Failed to read count")?;
|
||||
let count = u32::from_le_bytes(count_buf);
|
||||
|
||||
let mut features = HashMap::new();
|
||||
for _ in 0..count {
|
||||
let mut id_buf = [0u8; 4];
|
||||
let mut value_buf = [0u8; 8];
|
||||
|
||||
file.read_exact(&mut id_buf)
|
||||
.with_context(|| "Failed to read feature id")?;
|
||||
file.read_exact(&mut value_buf)
|
||||
.with_context(|| "Failed to read feature value")?;
|
||||
|
||||
let id = u32::from_le_bytes(id_buf);
|
||||
let value = u64::from_le_bytes(value_buf);
|
||||
|
||||
features.insert(id, value);
|
||||
}
|
||||
|
||||
log::info!("Loaded {} features from config", features.len());
|
||||
Ok(features)
|
||||
}
|
||||
|
||||
pub fn save_binary_config(features: &HashMap<u32, u64>) -> Result<()> {
|
||||
crate::utils::ensure_dir_exists(Path::new(defs::WORKING_DIR))?;
|
||||
|
||||
let path = Path::new(FEATURE_CONFIG_PATH);
|
||||
let mut file = File::create(path).with_context(|| "Failed to create feature config")?;
|
||||
|
||||
file.write_all(&FEATURE_MAGIC.to_le_bytes())
|
||||
.with_context(|| "Failed to write magic")?;
|
||||
|
||||
file.write_all(&FEATURE_VERSION.to_le_bytes())
|
||||
.with_context(|| "Failed to write version")?;
|
||||
|
||||
let count = features.len() as u32;
|
||||
file.write_all(&count.to_le_bytes())
|
||||
.with_context(|| "Failed to write count")?;
|
||||
|
||||
for (&id, &value) in features.iter() {
|
||||
file.write_all(&id.to_le_bytes())
|
||||
.with_context(|| format!("Failed to write feature id {}", id))?;
|
||||
file.write_all(&value.to_le_bytes())
|
||||
.with_context(|| format!("Failed to write feature value for id {}", id))?;
|
||||
}
|
||||
|
||||
file.sync_all()
|
||||
.with_context(|| "Failed to sync feature config")?;
|
||||
|
||||
log::info!("Saved {} features to config", features.len());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn apply_config(features: &HashMap<u32, u64>) -> Result<()> {
|
||||
log::info!("Applying feature configuration to kernel...");
|
||||
|
||||
let mut applied = 0;
|
||||
for (&id, &value) in features.iter() {
|
||||
match crate::ksucalls::set_feature(id, value) {
|
||||
Ok(_) => {
|
||||
if let Some(feature_id) = FeatureId::from_u32(id) {
|
||||
log::info!("Set feature {} to {}", feature_id.name(), value);
|
||||
} else {
|
||||
log::info!("Set feature {} to {}", id, value);
|
||||
}
|
||||
applied += 1;
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("Failed to set feature {}: {}", id, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("Applied {} features successfully", applied);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_feature(id: String) -> Result<()> {
|
||||
let feature_id = parse_feature_id(&id)?;
|
||||
let (value, supported) = crate::ksucalls::get_feature(feature_id as u32)
|
||||
.with_context(|| format!("Failed to get feature {}", id))?;
|
||||
|
||||
if !supported {
|
||||
println!("Feature '{}' is not supported by kernel", id);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("Feature: {} ({})", feature_id.name(), feature_id as u32);
|
||||
println!("Description: {}", feature_id.description());
|
||||
println!("Value: {}", value);
|
||||
println!(
|
||||
"Status: {}",
|
||||
if value != 0 { "enabled" } else { "disabled" }
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_feature(id: String, value: u64) -> Result<()> {
|
||||
let feature_id = parse_feature_id(&id)?;
|
||||
|
||||
crate::ksucalls::set_feature(feature_id as u32, value)
|
||||
.with_context(|| format!("Failed to set feature {} to {}", id, value))?;
|
||||
|
||||
println!(
|
||||
"Feature '{}' set to {} ({})",
|
||||
feature_id.name(),
|
||||
value,
|
||||
if value != 0 { "enabled" } else { "disabled" }
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn list_features() -> Result<()> {
|
||||
println!("Available Features:");
|
||||
println!("{}", "=".repeat(80));
|
||||
|
||||
let all_features = [FeatureId::SuCompat, FeatureId::KernelUmount];
|
||||
|
||||
for feature_id in all_features.iter() {
|
||||
let id = *feature_id as u32;
|
||||
let (value, supported) = crate::ksucalls::get_feature(id).unwrap_or((0, false));
|
||||
|
||||
let status = if !supported {
|
||||
"NOT_SUPPORTED".to_string()
|
||||
} else if value != 0 {
|
||||
format!("ENABLED ({})", value)
|
||||
} else {
|
||||
"DISABLED".to_string()
|
||||
};
|
||||
|
||||
println!("[{}] {} (ID={})", status, feature_id.name(), id);
|
||||
println!(" {}", feature_id.description());
|
||||
println!();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_config_and_apply() -> Result<()> {
|
||||
let features = load_binary_config()?;
|
||||
|
||||
if features.is_empty() {
|
||||
println!("No features found in config file");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
apply_config(&features)?;
|
||||
println!("Feature configuration loaded and applied");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn save_config() -> Result<()> {
|
||||
let mut features = HashMap::new();
|
||||
|
||||
let all_features = [FeatureId::SuCompat, FeatureId::KernelUmount];
|
||||
|
||||
for feature_id in all_features.iter() {
|
||||
let id = *feature_id as u32;
|
||||
if let Ok((value, supported)) = crate::ksucalls::get_feature(id)
|
||||
&& supported {
|
||||
features.insert(id, value);
|
||||
log::info!("Saved feature {} = {}", feature_id.name(), value);
|
||||
}
|
||||
}
|
||||
|
||||
save_binary_config(&features)?;
|
||||
println!(
|
||||
"Current feature states saved to config file ({} features)",
|
||||
features.len()
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn init_features() -> Result<()> {
|
||||
log::info!("Initializing features from config...");
|
||||
|
||||
let features = load_binary_config()?;
|
||||
|
||||
if features.is_empty() {
|
||||
log::info!("No feature config found, skipping initialization");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
apply_config(&features)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -87,6 +87,11 @@ pub fn on_post_data_fs() -> Result<()> {
|
||||
warn!("apply root profile sepolicy failed: {e}");
|
||||
}
|
||||
|
||||
// load feature config
|
||||
if let Err(e) = crate::feature::init_features() {
|
||||
warn!("init features failed: {e}");
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
if let Err(e) = kpm::start_kpm_watcher() {
|
||||
warn!("KPM: Failed to start KPM watcher: {e}");
|
||||
|
||||
@@ -8,11 +8,13 @@ const EVENT_POST_FS_DATA: u32 = 1;
|
||||
const EVENT_BOOT_COMPLETED: u32 = 2;
|
||||
const EVENT_MODULE_MOUNTED: u32 = 3;
|
||||
|
||||
const KSU_IOCTL_GRANT_ROOT: u32 = 0x4B01; // _IO('K', 1)
|
||||
const KSU_IOCTL_GRANT_ROOT: u32 = 0x00004b01; // _IO('K', 1)
|
||||
const KSU_IOCTL_GET_INFO: u32 = 0x80084b02; // _IOR('K', 2, struct ksu_get_info_cmd)
|
||||
const KSU_IOCTL_REPORT_EVENT: u32 = 0x40044b03; // _IOW('K', 3, struct ksu_report_event_cmd)
|
||||
const KSU_IOCTL_REPORT_EVENT: u32 = 0x40084b03; // _IOW('K', 3, struct ksu_report_event_cmd)
|
||||
const KSU_IOCTL_SET_SEPOLICY: u32 = 0xc0104b04; // _IOWR('K', 4, struct ksu_set_sepolicy_cmd)
|
||||
const KSU_IOCTL_CHECK_SAFEMODE: u32 = 0x80014b05; // _IOR('K', 5, struct ksu_check_safemode_cmd)
|
||||
const KSU_IOCTL_GET_FEATURE: u32 = 0xc00c4b0d; // _IOWR('K', 13, struct ksu_get_feature_cmd)
|
||||
const KSU_IOCTL_SET_FEATURE: u32 = 0x40084b0e; // _IOW('K', 14, struct ksu_set_feature_cmd)
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
@@ -39,6 +41,21 @@ struct CheckSafemodeCmd {
|
||||
in_safe_mode: u8,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
struct GetFeatureCmd {
|
||||
feature_id: u32,
|
||||
value: u64,
|
||||
supported: u8,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
struct SetFeatureCmd {
|
||||
feature_id: u32,
|
||||
value: u64,
|
||||
}
|
||||
|
||||
// Global driver fd cache
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
static DRIVER_FD: OnceLock<RawFd> = OnceLock::new();
|
||||
@@ -184,3 +201,22 @@ pub fn set_sepolicy(cmd: &SetSepolicyCmd) -> std::io::Result<()> {
|
||||
ksuctl(KSU_IOCTL_SET_SEPOLICY, &mut ioctl_cmd as *mut _)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get feature value and support status from kernel
|
||||
/// Returns (value, supported)
|
||||
pub fn get_feature(feature_id: u32) -> std::io::Result<(u64, bool)> {
|
||||
let mut cmd = GetFeatureCmd {
|
||||
feature_id,
|
||||
value: 0,
|
||||
supported: 0,
|
||||
};
|
||||
ksuctl(KSU_IOCTL_GET_FEATURE, &mut cmd as *mut _)?;
|
||||
Ok((cmd.value, cmd.supported != 0))
|
||||
}
|
||||
|
||||
/// Set feature value in kernel
|
||||
pub fn set_feature(feature_id: u32, value: u64) -> std::io::Result<()> {
|
||||
let mut cmd = SetFeatureCmd { feature_id, value };
|
||||
ksuctl(KSU_IOCTL_SET_FEATURE, &mut cmd as *mut _)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ mod boot_patch;
|
||||
mod cli;
|
||||
mod debug;
|
||||
mod defs;
|
||||
mod feature;
|
||||
mod init_event;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
mod kpm;
|
||||
|
||||
Reference in New Issue
Block a user