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"
|
- version: "android15-6.6"
|
||||||
sub_level: 98
|
sub_level: 98
|
||||||
os_patch_level: 2025-09
|
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-mock.yml when debugging
|
||||||
uses: ./.github/workflows/gki-kernel.yml
|
uses: ./.github/workflows/gki-kernel.yml
|
||||||
with:
|
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 += throne_tracker.o
|
||||||
kernelsu-objs += core_hook.o
|
kernelsu-objs += core_hook.o
|
||||||
kernelsu-objs += supercalls.o
|
kernelsu-objs += supercalls.o
|
||||||
|
kernelsu-objs += feature.o
|
||||||
kernelsu-objs += ksud.o
|
kernelsu-objs += ksud.o
|
||||||
kernelsu-objs += embed_ksud.o
|
kernelsu-objs += embed_ksud.o
|
||||||
kernelsu-objs += kernel_compat.o
|
kernelsu-objs += kernel_compat.o
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
#include "core_hook.h"
|
#include "core_hook.h"
|
||||||
|
#include "feature.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "ksu.h"
|
#include "ksu.h"
|
||||||
#include "ksud.h"
|
#include "ksud.h"
|
||||||
@@ -78,6 +79,29 @@ struct ksu_umount_work {
|
|||||||
struct mnt_namespace *mnt_ns;
|
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()
|
static inline bool is_allow_su()
|
||||||
{
|
{
|
||||||
if (is_manager()) {
|
if (is_manager()) {
|
||||||
@@ -539,8 +563,8 @@ static void do_umount_work(struct work_struct *work)
|
|||||||
// try umount ksu temp path
|
// try umount ksu temp path
|
||||||
try_umount("/debug_ramdisk", false, MNT_DETACH);
|
try_umount("/debug_ramdisk", false, MNT_DETACH);
|
||||||
|
|
||||||
|
// fixme: dec refcount
|
||||||
current->nsproxy->mnt_ns = old_mnt_ns;
|
current->nsproxy->mnt_ns = old_mnt_ns;
|
||||||
put_mnt_ns(umount_work->mnt_ns);
|
|
||||||
|
|
||||||
kfree(umount_work);
|
kfree(umount_work);
|
||||||
}
|
}
|
||||||
@@ -588,6 +612,10 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ksu_kernel_umount_enabled) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (!ksu_uid_should_umount(new_uid.val)) {
|
if (!ksu_uid_should_umount(new_uid.val)) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
@@ -622,8 +650,8 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fixme: inc refcount
|
||||||
umount_work->mnt_ns = current->nsproxy->mnt_ns;
|
umount_work->mnt_ns = current->nsproxy->mnt_ns;
|
||||||
get_mnt_ns(umount_work->mnt_ns);
|
|
||||||
|
|
||||||
INIT_WORK(&umount_work->work, do_umount_work);
|
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
|
// 2. security_task_fix_setuid hook for handling setuid
|
||||||
static int security_task_fix_setuid_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
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(regs);
|
||||||
struct cred *new = (struct cred *)PT_REGS_PARM1(real_regs);
|
const struct cred *old = (const struct cred *)PT_REGS_PARM2(regs);
|
||||||
const struct cred *old = (const struct cred *)PT_REGS_PARM2(real_regs);
|
|
||||||
|
|
||||||
ksu_handle_setuid(new, old);
|
ksu_handle_setuid(new, old);
|
||||||
|
|
||||||
@@ -844,6 +871,10 @@ __maybe_unused int ksu_kprobe_exit(void)
|
|||||||
|
|
||||||
void __init ksu_core_init(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);
|
ksu_workqueue = alloc_workqueue("ksu_umount", WQ_UNBOUND, 0);
|
||||||
if (!ksu_workqueue) {
|
if (!ksu_workqueue) {
|
||||||
pr_err("Failed to create ksu workqueue\n");
|
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 "allowlist.h"
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
#include "core_hook.h"
|
#include "core_hook.h"
|
||||||
|
#include "feature.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "ksu.h"
|
#include "ksu.h"
|
||||||
#include "throne_tracker.h"
|
#include "throne_tracker.h"
|
||||||
@@ -26,6 +27,7 @@ extern void ksu_sucompat_init();
|
|||||||
extern void ksu_sucompat_exit();
|
extern void ksu_sucompat_exit();
|
||||||
extern void ksu_ksud_init();
|
extern void ksu_ksud_init();
|
||||||
extern void ksu_ksud_exit();
|
extern void ksu_ksud_exit();
|
||||||
|
extern void ksu_supercalls_init();
|
||||||
#ifdef CONFIG_KSU_TRACEPOINT_HOOK
|
#ifdef CONFIG_KSU_TRACEPOINT_HOOK
|
||||||
extern void ksu_trace_register();
|
extern void ksu_trace_register();
|
||||||
extern void ksu_trace_unregister();
|
extern void ksu_trace_unregister();
|
||||||
@@ -43,6 +45,10 @@ int __init kernelsu_init(void)
|
|||||||
pr_alert("*************************************************************");
|
pr_alert("*************************************************************");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
ksu_feature_init();
|
||||||
|
|
||||||
|
ksu_supercalls_init();
|
||||||
|
|
||||||
ksu_core_init();
|
ksu_core_init();
|
||||||
|
|
||||||
ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
|
ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
|
||||||
@@ -89,6 +95,7 @@ void kernelsu_exit(void)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
ksu_core_exit();
|
ksu_core_exit();
|
||||||
|
ksu_feature_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(kernelsu_init);
|
module_init(kernelsu_init);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "objsec.h"
|
#include "objsec.h"
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
|
#include "feature.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "ksud.h"
|
#include "ksud.h"
|
||||||
#include "kernel_compat.h"
|
#include "kernel_compat.h"
|
||||||
@@ -22,6 +23,44 @@
|
|||||||
#define SH_PATH "/system/bin/sh"
|
#define SH_PATH "/system/bin/sh"
|
||||||
|
|
||||||
extern void escape_to_root();
|
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
|
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||||
static bool ksu_sucompat_hook_state __read_mostly = true;
|
static bool ksu_sucompat_hook_state __read_mostly = true;
|
||||||
@@ -340,7 +379,7 @@ static void destroy_kprobe(struct kprobe **kp_ptr)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// sucompat: permited process can execute 'su' to gain root access.
|
// sucompat: permited process can execute 'su' to gain root access.
|
||||||
void ksu_sucompat_init()
|
void ksu_sucompat_enable()
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||||
su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre);
|
su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre);
|
||||||
@@ -353,7 +392,7 @@ void ksu_sucompat_init()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_sucompat_exit()
|
void ksu_sucompat_disable()
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||||
int i;
|
int i;
|
||||||
@@ -365,3 +404,23 @@ void ksu_sucompat_exit()
|
|||||||
pr_info("ksu_sucompat_exit: hooks disabled: execve/execveat_su, faccessat, stat\n");
|
pr_info("ksu_sucompat_exit: hooks disabled: execve/execveat_su, faccessat, stat\n");
|
||||||
#endif
|
#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 <linux/version.h>
|
||||||
|
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
|
#include "feature.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "ksud.h"
|
#include "ksud.h"
|
||||||
#include "manager.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_init(void);
|
||||||
extern void ksu_sucompat_exit(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;
|
bool ksu_uid_scanner_enabled = false;
|
||||||
|
|
||||||
// Permission check functions
|
// Permission check functions
|
||||||
bool perm_check_manager(void)
|
bool only_manager(void)
|
||||||
{
|
{
|
||||||
return is_manager();
|
return is_manager();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool perm_check_root(void)
|
bool only_root(void)
|
||||||
{
|
{
|
||||||
return current_uid().val == 0;
|
return current_uid().val == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool perm_check_basic(void)
|
bool manager_or_root(void)
|
||||||
{
|
{
|
||||||
return current_uid().val == 0 || is_manager();
|
return current_uid().val == 0 || is_manager();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool perm_check_all(void)
|
bool always_allow(void)
|
||||||
{
|
{
|
||||||
return true; // No permission check
|
return true; // No permission check
|
||||||
}
|
}
|
||||||
@@ -101,6 +98,7 @@ static int do_get_info(void __user *arg)
|
|||||||
if (is_manager()) {
|
if (is_manager()) {
|
||||||
cmd.flags |= 0x2;
|
cmd.flags |= 0x2;
|
||||||
}
|
}
|
||||||
|
cmd.features = KSU_FEATURE_MAX;
|
||||||
|
|
||||||
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||||
pr_err("get_version: copy_to_user failed\n");
|
pr_err("get_version: copy_to_user failed\n");
|
||||||
@@ -318,42 +316,49 @@ static int do_set_app_profile(void __user *arg)
|
|||||||
return 0;
|
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;
|
struct ksu_get_feature_cmd cmd;
|
||||||
|
bool supported;
|
||||||
|
int ret;
|
||||||
|
|
||||||
cmd.enabled = ksu_su_compat_enabled;
|
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))) {
|
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||||
pr_err("is_su_enabled: copy_to_user failed\n");
|
pr_err("get_feature: copy_to_user failed\n");
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_enable_su(void __user *arg)
|
static int do_set_feature(void __user *arg)
|
||||||
{
|
{
|
||||||
struct ksu_enable_su_cmd cmd;
|
struct ksu_set_feature_cmd cmd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||||
pr_err("enable_su: copy_from_user failed\n");
|
pr_err("set_feature: copy_from_user failed\n");
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd.enable == ksu_su_compat_enabled) {
|
ret = ksu_set_feature(cmd.feature_id, cmd.value);
|
||||||
pr_info("enable_su: no need to change\n");
|
if (ret) {
|
||||||
return 0;
|
pr_err("set_feature: failed for feature %u: %d\n", cmd.feature_id, ret);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd.enable) {
|
|
||||||
ksu_sucompat_init();
|
|
||||||
} else {
|
|
||||||
ksu_sucompat_exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
ksu_su_compat_enabled = cmd.enable;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -517,80 +522,79 @@ static int do_enable_uid_scanner(void __user *arg)
|
|||||||
|
|
||||||
// IOCTL handlers mapping table
|
// IOCTL handlers mapping table
|
||||||
static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = {
|
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_GRANT_ROOT, .name = "GRANT_ROOT", .handler = do_grant_root, .perm_check = manager_or_root },
|
||||||
{ .cmd = KSU_IOCTL_GET_INFO, .handler = do_get_info, .perm_check = perm_check_all, .name = "do_get_info"},
|
{ .cmd = KSU_IOCTL_GET_INFO, .name = "GET_INFO", .handler = do_get_info, .perm_check = always_allow },
|
||||||
{ .cmd = KSU_IOCTL_REPORT_EVENT, .handler = do_report_event, .perm_check = perm_check_root, .name = "do_report_event"},
|
{ .cmd = KSU_IOCTL_REPORT_EVENT, .name = "REPORT_EVENT", .handler = do_report_event, .perm_check = only_root },
|
||||||
{ .cmd = KSU_IOCTL_SET_SEPOLICY, .handler = do_set_sepolicy, .perm_check = perm_check_root, .name = "do_set_sepolicy"},
|
{ .cmd = KSU_IOCTL_SET_SEPOLICY, .name = "SET_SEPOLICY", .handler = do_set_sepolicy, .perm_check = only_root },
|
||||||
{ .cmd = KSU_IOCTL_CHECK_SAFEMODE, .handler = do_check_safemode, .perm_check = perm_check_all, .name = "do_check_safemode"},
|
{ .cmd = KSU_IOCTL_CHECK_SAFEMODE, .name = "CHECK_SAFEMODE", .handler = do_check_safemode, .perm_check = always_allow },
|
||||||
{ .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_ALLOW_LIST, .name = "GET_ALLOW_LIST", .handler = do_get_allow_list, .perm_check = manager_or_root },
|
||||||
{ .cmd = KSU_IOCTL_GET_DENY_LIST, .handler = do_get_deny_list, .perm_check = perm_check_basic, .name = "do_get_deny_list"},
|
{ .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, .handler = do_uid_granted_root, .perm_check = perm_check_basic, .name = "do_uid_granted_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, .handler = do_uid_should_umount, .perm_check = perm_check_basic, .name = "do_uid_should_umount"},
|
{ .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, .handler = do_get_manager_uid, .perm_check = perm_check_basic, .name = "do_get_manager_uid"},
|
{ .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, .handler = do_get_app_profile, .perm_check = perm_check_manager, .name = "do_get_app_profile"},
|
{ .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, .handler = do_set_app_profile, .perm_check = perm_check_manager, .name = "do_set_app_profile"},
|
{ .cmd = KSU_IOCTL_SET_APP_PROFILE, .name = "SET_APP_PROFILE", .handler = do_set_app_profile, .perm_check = only_manager },
|
||||||
{ .cmd = KSU_IOCTL_IS_SU_ENABLED, .handler = do_is_su_enabled, .perm_check = perm_check_manager, .name = "do_is_su_enabled"},
|
{ .cmd = KSU_IOCTL_GET_FEATURE, .name = "GET_FEATURE", .handler = do_get_feature, .perm_check = manager_or_root },
|
||||||
{ .cmd = KSU_IOCTL_ENABLE_SU, .handler = do_enable_su, .perm_check = perm_check_manager, .name = "do_enable_su"},
|
{ .cmd = KSU_IOCTL_SET_FEATURE, .name = "SET_FEATURE", .handler = do_set_feature, .perm_check = manager_or_root },
|
||||||
{ .cmd = KSU_IOCTL_GET_FULL_VERSION, .handler = do_get_full_version, .perm_check = perm_check_manager, .name = "do_get_full_version"},
|
{ .cmd = KSU_IOCTL_GET_FULL_VERSION,.name = "GET_FULL_VERSION", .handler = do_get_full_version, .perm_check = always_allow},
|
||||||
{ .cmd = KSU_IOCTL_HOOK_TYPE, .handler = do_get_hook_type, .perm_check = perm_check_basic, .name = "do_get_hook_type"},
|
{ .cmd = KSU_IOCTL_HOOK_TYPE,.name = "GET_HOOK_TYPE", .handler = do_get_hook_type, .perm_check = manager_or_root},
|
||||||
{ .cmd = KSU_IOCTL_ENABLE_KPM, .handler = do_enable_kpm, .perm_check = perm_check_basic, .name = "do_enable_kpm"},
|
{ .cmd = KSU_IOCTL_ENABLE_KPM, .name = "GET_ENABLE_KPM", .handler = do_enable_kpm, .perm_check = manager_or_root},
|
||||||
{ .cmd = KSU_IOCTL_DYNAMIC_MANAGER, .handler = do_dynamic_manager, .perm_check = perm_check_basic, .name = "do_dynamic_manager"},
|
{ .cmd = KSU_IOCTL_DYNAMIC_MANAGER, .name = "SET_DYNAMIC_MANAGER", .handler = do_dynamic_manager, .perm_check = manager_or_root},
|
||||||
{ .cmd = KSU_IOCTL_GET_MANAGERS, .handler = do_get_managers, .perm_check = perm_check_basic, .name = "do_get_managers"},
|
{ .cmd = KSU_IOCTL_GET_MANAGERS, .name = "GET_MANAGERS", .handler = do_get_managers, .perm_check = manager_or_root},
|
||||||
{ .cmd = KSU_IOCTL_ENABLE_UID_SCANNER, .handler = do_enable_uid_scanner, .perm_check = perm_check_basic, .name = "do_enable_uid_scanner"},
|
{ .cmd = KSU_IOCTL_ENABLE_UID_SCANNER, .name = "SET_ENABLE_UID_SCANNER", .handler = do_enable_uid_scanner, .perm_check = manager_or_root},
|
||||||
{ .cmd = 0, .handler = NULL, .perm_check = NULL, .name = NULL} // Sentinel
|
{ .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
|
// IOCTL dispatcher
|
||||||
static long anon_ksu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
static long anon_ksu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||||
{
|
{
|
||||||
void __user *argp = (void __user *)arg;
|
void __user *argp = (void __user *)arg;
|
||||||
int i;
|
int i;
|
||||||
const char *cmd_name = "unknown";
|
|
||||||
int ret = -ENOTTY;
|
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_info("ksu ioctl: cmd=0x%x from uid=%d\n", cmd, current_uid().val);
|
pr_info("ksu ioctl: cmd=0x%x from uid=%d\n", cmd, current_uid().val);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Determine the command name based on the cmd value
|
|
||||||
for (i = 0; ksu_ioctl_handlers[i].handler; i++) {
|
for (i = 0; ksu_ioctl_handlers[i].handler; i++) {
|
||||||
if (cmd == ksu_ioctl_handlers[i].cmd) {
|
if (cmd == ksu_ioctl_handlers[i].cmd) {
|
||||||
cmd_name = ksu_ioctl_handlers[i].name;
|
// Check permission first
|
||||||
break;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check permission first
|
pr_warn("ksu ioctl: unsupported command 0x%x\n", cmd);
|
||||||
if (ksu_ioctl_handlers[i].perm_check &&
|
return -ENOTTY;
|
||||||
!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
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// File release handler
|
// File release handler
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ struct ksu_become_daemon_cmd {
|
|||||||
struct ksu_get_info_cmd {
|
struct ksu_get_info_cmd {
|
||||||
__u32 version; // Output: KERNEL_SU_VERSION
|
__u32 version; // Output: KERNEL_SU_VERSION
|
||||||
__u32 flags; // Output: flags (bit 0: MODULE mode)
|
__u32 flags; // Output: flags (bit 0: MODULE mode)
|
||||||
|
__u32 features; // Output: max feature ID supported
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ksu_report_event_cmd {
|
struct ksu_report_event_cmd {
|
||||||
@@ -61,12 +62,15 @@ struct ksu_set_app_profile_cmd {
|
|||||||
struct app_profile profile; // Input: app profile structure
|
struct app_profile profile; // Input: app profile structure
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ksu_is_su_enabled_cmd {
|
struct ksu_get_feature_cmd {
|
||||||
__u8 enabled; // Output: true if su compat enabled
|
__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 {
|
struct ksu_set_feature_cmd {
|
||||||
__u8 enable; // Input: true to enable, false to disable
|
__u32 feature_id; // Input: feature ID (enum ksu_feature_id)
|
||||||
|
__u64 value; // Input: feature value/state to set
|
||||||
};
|
};
|
||||||
|
|
||||||
// Other command structures
|
// 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_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_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_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_GET_FEATURE _IOWR('K', 13, struct ksu_get_feature_cmd)
|
||||||
#define KSU_IOCTL_ENABLE_SU _IOW('K', 14, struct ksu_enable_su_cmd)
|
#define KSU_IOCTL_SET_FEATURE _IOW('K', 14, struct ksu_set_feature_cmd)
|
||||||
// Other IOCTL command definitions
|
// Other IOCTL command definitions
|
||||||
#define KSU_IOCTL_GET_FULL_VERSION _IOR('K', 100, struct ksu_get_full_version_cmd)
|
#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)
|
#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 int (*ksu_ioctl_handler_t)(void __user *arg);
|
||||||
typedef bool (*ksu_perm_check_t)(void);
|
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
|
// IOCTL command mapping
|
||||||
struct ksu_ioctl_cmd_map {
|
struct ksu_ioctl_cmd_map {
|
||||||
unsigned int cmd;
|
unsigned int cmd;
|
||||||
|
const char *name;
|
||||||
ksu_ioctl_handler_t handler;
|
ksu_ioctl_handler_t handler;
|
||||||
ksu_perm_check_t perm_check; // Permission check function
|
ksu_perm_check_t perm_check; // Permission check function
|
||||||
const char *name; // Command name for logging
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Install KSU fd to current process
|
// Install KSU fd to current process
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <linux/capability.h>
|
||||||
|
|
||||||
NativeBridgeNP(getVersion, jint) {
|
NativeBridgeNP(getVersion, jint) {
|
||||||
uint32_t version = get_version();
|
uint32_t version = get_version();
|
||||||
@@ -300,6 +301,14 @@ NativeBridge(setSuEnabled, jboolean, jboolean enabled) {
|
|||||||
return set_su_enabled(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
|
// Check if KPM is enabled
|
||||||
NativeBridgeNP(isKPMEnabled, jboolean) {
|
NativeBridgeNP(isKPMEnabled, jboolean) {
|
||||||
return is_KPM_enable();
|
return is_KPM_enable();
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
|
|
||||||
@@ -133,13 +134,56 @@ int get_app_profile(struct app_profile *profile) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool set_su_enabled(bool enabled) {
|
bool set_su_enabled(bool enabled) {
|
||||||
struct ksu_enable_su_cmd cmd = {.enable = enabled};
|
struct ksu_set_feature_cmd cmd = {};
|
||||||
return ksuctl(KSU_IOCTL_ENABLE_SU, &cmd) == 0;
|
cmd.feature_id = KSU_FEATURE_SU_COMPAT;
|
||||||
|
cmd.value = enabled ? 1 : 0;
|
||||||
|
return ksuctl(KSU_IOCTL_SET_FEATURE, &cmd) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_su_enabled() {
|
bool is_su_enabled() {
|
||||||
struct ksu_is_su_enabled_cmd cmd = {};
|
struct ksu_get_feature_cmd cmd = {};
|
||||||
return ksuctl(KSU_IOCTL_IS_SU_ENABLED, &cmd) == 0 && cmd.enabled;
|
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) {
|
void get_full_version(char* buff) {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#define KERNELSU_KSU_H
|
#define KERNELSU_KSU_H
|
||||||
|
|
||||||
#include "prelude.h"
|
#include "prelude.h"
|
||||||
#include <linux/capability.h>
|
#include <stdint.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/ioctl.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);
|
int get_app_profile(struct app_profile* profile);
|
||||||
|
|
||||||
bool set_su_enabled(bool enabled);
|
|
||||||
|
|
||||||
bool is_su_enabled();
|
|
||||||
|
|
||||||
bool is_KPM_enable();
|
bool is_KPM_enable();
|
||||||
|
|
||||||
void get_hook_type(char* hook_type);
|
void get_hook_type(char* hook_type);
|
||||||
@@ -129,50 +125,69 @@ bool set_uid_scanner_enabled(bool enabled);
|
|||||||
|
|
||||||
bool clear_uid_scanner_environment();
|
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 {
|
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 {
|
struct ksu_get_info_cmd {
|
||||||
__u32 version; // Output: KERNEL_SU_VERSION
|
uint32_t version; // Output: KERNEL_SU_VERSION
|
||||||
__u32 flags; // Output: flags (bit 0: MODULE mode)
|
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 {
|
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 {
|
struct ksu_set_sepolicy_cmd {
|
||||||
__u64 cmd; // Input: sepolicy command
|
uint64_t cmd; // Input: sepolicy command
|
||||||
__aligned_u64 arg; // Input: sepolicy argument pointer
|
uint64_t arg; // Input: sepolicy argument pointer
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ksu_check_safemode_cmd {
|
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 {
|
struct ksu_get_allow_list_cmd {
|
||||||
__u32 uids[128]; // Output: array of allowed/denied UIDs
|
uint32_t uids[128]; // Output: array of allowed/denied UIDs
|
||||||
__u32 count; // Output: number of UIDs in array
|
uint32_t count; // Output: number of UIDs in array
|
||||||
__u8 allow; // Input: true for allow list, false for deny list
|
uint8_t allow; // Input: true for allow list, false for deny list
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ksu_uid_granted_root_cmd {
|
struct ksu_uid_granted_root_cmd {
|
||||||
__u32 uid; // Input: target UID to check
|
uint32_t uid; // Input: target UID to check
|
||||||
__u8 granted; // Output: true if granted, false otherwise
|
uint8_t granted; // Output: true if granted, false otherwise
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ksu_uid_should_umount_cmd {
|
struct ksu_uid_should_umount_cmd {
|
||||||
__u32 uid; // Input: target UID to check
|
uint32_t uid; // Input: target UID to check
|
||||||
__u8 should_umount; // Output: true if should umount, false otherwise
|
uint8_t should_umount; // Output: true if should umount, false otherwise
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ksu_get_manager_uid_cmd {
|
struct ksu_get_manager_uid_cmd {
|
||||||
__u32 uid; // Output: manager UID
|
uint32_t uid; // Output: manager UID
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ksu_set_manager_uid_cmd {
|
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 {
|
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 app_profile profile; // Input: app profile structure
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ksu_is_su_enabled_cmd {
|
// Su compat
|
||||||
__u8 enabled; // Output: true if su compat enabled
|
bool set_su_enabled(bool enabled);
|
||||||
};
|
bool is_su_enabled();
|
||||||
|
|
||||||
struct ksu_enable_su_cmd {
|
// Kernel umount
|
||||||
__u8 enable; // Input: true to enable, false to disable
|
bool set_kernel_umount_enabled(bool enabled);
|
||||||
};
|
bool is_kernel_umount_enabled();
|
||||||
|
|
||||||
// Other command structures
|
// Other command structures
|
||||||
struct ksu_get_full_version_cmd {
|
struct ksu_get_full_version_cmd {
|
||||||
@@ -201,7 +216,7 @@ struct ksu_hook_type_cmd {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct ksu_enable_kpm_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 {
|
struct ksu_dynamic_manager_cmd {
|
||||||
@@ -213,9 +228,9 @@ struct ksu_get_managers_cmd {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct ksu_enable_uid_scanner_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)
|
uint32_t 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)
|
uint32_t enabled; // Input: enable or disable (for UID_SCANNER_OP_TOGGLE)
|
||||||
__u64 status_ptr; // Input: pointer to store status (for UID_SCANNER_OP_GET_STATUS)
|
uint64_t status_ptr; // Input: pointer to store status (for UID_SCANNER_OP_GET_STATUS)
|
||||||
};
|
};
|
||||||
|
|
||||||
// IOCTL command definitions
|
// 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_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_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_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_GET_FEATURE _IOWR('K', 13, struct ksu_get_feature_cmd)
|
||||||
#define KSU_IOCTL_ENABLE_SU _IOW('K', 14, struct ksu_enable_su_cmd)
|
#define KSU_IOCTL_SET_FEATURE _IOW('K', 14, struct ksu_set_feature_cmd)
|
||||||
|
|
||||||
// Other IOCTL command definitions
|
// Other IOCTL command definitions
|
||||||
#define KSU_IOCTL_GET_FULL_VERSION _IOR('K', 100, struct ksu_get_full_version_cmd)
|
#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 isSuEnabled(): Boolean
|
||||||
external fun setSuEnabled(enabled: Boolean): 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 isKPMEnabled(): Boolean
|
||||||
external fun getHookType(): String
|
external fun getHookType(): String
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import androidx.compose.foundation.selection.selectable
|
|||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
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.AutoFixHigh
|
||||||
import androidx.compose.material.icons.filled.FileUpload
|
import androidx.compose.material.icons.filled.FileUpload
|
||||||
import androidx.compose.material.icons.filled.Security
|
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())
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopBar(
|
TopBar(
|
||||||
onBack = { navigator.popBackStack() },
|
onBack = { navigator.popBackStack() },
|
||||||
|
onLkmUpload = onLkmUpload,
|
||||||
scrollBehavior = scrollBehavior
|
scrollBehavior = scrollBehavior
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -934,6 +952,7 @@ fun rememberSelectKmiDialog(onSelected: (String?) -> Unit): DialogHandle {
|
|||||||
@Composable
|
@Composable
|
||||||
private fun TopBar(
|
private fun TopBar(
|
||||||
onBack: () -> Unit = {},
|
onBack: () -> Unit = {},
|
||||||
|
onLkmUpload: () -> Unit = {},
|
||||||
scrollBehavior: TopAppBarScrollBehavior? = null
|
scrollBehavior: TopAppBarScrollBehavior? = null
|
||||||
) {
|
) {
|
||||||
val colorScheme = MaterialTheme.colorScheme
|
val colorScheme = MaterialTheme.colorScheme
|
||||||
@@ -966,6 +985,17 @@ private fun TopBar(
|
|||||||
windowInsets = WindowInsets.safeDrawing.only(
|
windowInsets = WindowInsets.safeDrawing.only(
|
||||||
WindowInsetsSides.Top + WindowInsetsSides.Horizontal
|
WindowInsetsSides.Top + WindowInsetsSides.Horizontal
|
||||||
),
|
),
|
||||||
|
actions = {
|
||||||
|
IconButton(
|
||||||
|
modifier = Modifier.padding(end = 16.dp),
|
||||||
|
onClick = onLkmUpload
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.AutoMirrored.Filled.Input,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
scrollBehavior = scrollBehavior
|
scrollBehavior = scrollBehavior
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import androidx.compose.foundation.verticalScroll
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.Undo
|
import androidx.compose.material.icons.automirrored.filled.Undo
|
||||||
import androidx.compose.material.icons.filled.*
|
import androidx.compose.material.icons.filled.*
|
||||||
|
import androidx.compose.material.icons.rounded.FolderDelete
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
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 {
|
var forceSignatureVerification by rememberSaveable {
|
||||||
mutableStateOf(prefs.getBoolean("force_signature_verification", false))
|
mutableStateOf(prefs.getBoolean("force_signature_verification", false))
|
||||||
|
|||||||
@@ -164,6 +164,8 @@
|
|||||||
<string name="su_not_allowed">不允许授予 %s 超级用户权限</string>
|
<string name="su_not_allowed">不允许授予 %s 超级用户权限</string>
|
||||||
<string name="settings_disable_su">禁用 su 兼容性</string>
|
<string name="settings_disable_su">禁用 su 兼容性</string>
|
||||||
<string name="settings_disable_su_summary">临时禁止任何应用程序通过 su 命令获取 Root 权限(现有的 Root 进程不受影响)</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="module_install_multiple_confirm_with_names">确定要安装以下 %1$d 个模块吗?\n\n%2$s</string>
|
||||||
<string name="more_settings">更多设置</string>
|
<string name="more_settings">更多设置</string>
|
||||||
<string name="selinux">SELinux</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="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">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_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="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="more_settings">More settings</string>
|
||||||
<string name="selinux">SELinux</string>
|
<string name="selinux">SELinux</string>
|
||||||
|
|||||||
@@ -64,6 +64,12 @@ enum Commands {
|
|||||||
command: Profile,
|
command: Profile,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Manage kernel features
|
||||||
|
Feature {
|
||||||
|
#[command(subcommand)]
|
||||||
|
command: Feature,
|
||||||
|
},
|
||||||
|
|
||||||
/// Patch boot or init_boot images to apply KernelSU
|
/// Patch boot or init_boot images to apply KernelSU
|
||||||
BootPatch {
|
BootPatch {
|
||||||
/// boot image path, if not specified, will try to find the boot image automatically
|
/// boot image path, if not specified, will try to find the boot image automatically
|
||||||
@@ -281,6 +287,32 @@ enum Profile {
|
|||||||
ListTemplates,
|
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")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
mod kpm_cmd {
|
mod kpm_cmd {
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
@@ -368,6 +400,14 @@ pub fn run() -> Result<()> {
|
|||||||
Profile::ListTemplates => crate::profile::list_templates(),
|
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 {
|
Commands::Debug { command } => match command {
|
||||||
Debug::SetManager { apk } => debug::set_manager(&apk),
|
Debug::SetManager { apk } => debug::set_manager(&apk),
|
||||||
Debug::GetSign { 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}");
|
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")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
if let Err(e) = kpm::start_kpm_watcher() {
|
if let Err(e) = kpm::start_kpm_watcher() {
|
||||||
warn!("KPM: Failed to start KPM watcher: {e}");
|
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_BOOT_COMPLETED: u32 = 2;
|
||||||
const EVENT_MODULE_MOUNTED: u32 = 3;
|
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_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_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_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)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Default)]
|
||||||
@@ -39,6 +41,21 @@ struct CheckSafemodeCmd {
|
|||||||
in_safe_mode: u8,
|
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
|
// Global driver fd cache
|
||||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||||
static DRIVER_FD: OnceLock<RawFd> = OnceLock::new();
|
static DRIVER_FD: OnceLock<RawFd> = OnceLock::new();
|
||||||
@@ -183,4 +200,23 @@ pub fn set_sepolicy(cmd: &SetSepolicyCmd) -> std::io::Result<()> {
|
|||||||
let mut ioctl_cmd = *cmd;
|
let mut ioctl_cmd = *cmd;
|
||||||
ksuctl(KSU_IOCTL_SET_SEPOLICY, &mut ioctl_cmd as *mut _)?;
|
ksuctl(KSU_IOCTL_SET_SEPOLICY, &mut ioctl_cmd as *mut _)?;
|
||||||
Ok(())
|
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 cli;
|
||||||
mod debug;
|
mod debug;
|
||||||
mod defs;
|
mod defs;
|
||||||
|
mod feature;
|
||||||
mod init_event;
|
mod init_event;
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
mod kpm;
|
mod kpm;
|
||||||
|
|||||||
Reference in New Issue
Block a user