From fb5117fed2068185407d562a41f4493ede9064b9 Mon Sep 17 00:00:00 2001 From: jingfelix Date: Tue, 17 Dec 2024 21:08:19 +0800 Subject: [PATCH] test: add benchmark with whatthepatch parse_patch Signed-off-by: jingfelix --- .gitignore | 1 - README.md | 26 + pdm.lock | 19 +- pyproject.toml | 3 +- requirements.txt | 3 +- src/Patche/commands/apply.py | 19 +- src/Patche/commands/show.py | 6 +- src/Patche/model.py | 6 +- src/Patche/utils/parse.py | 4 +- tests/bench_parse.py | 126 ++++ tests/cases/CVE-2017-11176.patch | 48 ++ tests/cases/CVE-2017-16995.patch | 48 ++ tests/cases/CVE-2017-18509.patch | 62 ++ tests/cases/CVE-2017-5123.patch | 45 ++ tests/cases/CVE-2018-25015.patch | 86 +++ tests/cases/CVE-2019-10639.patch | 77 +++ tests/cases/CVE-2019-11477.patch | 166 +++++ tests/cases/CVE-2019-11599.patch | 242 +++++++ tests/cases/CVE-2019-11815.patch | 144 +++++ tests/cases/CVE-2019-13233.patch | 173 +++++ tests/cases/CVE-2019-13272.patch | 55 ++ tests/cases/CVE-2019-15239.patch | 87 +++ tests/cases/CVE-2019-18198.patch | 80 +++ tests/cases/CVE-2019-18675.patch | 122 ++++ tests/cases/CVE-2019-18683.patch | 125 ++++ tests/cases/CVE-2019-18805.patch | 91 +++ tests/cases/CVE-2019-19241.patch | 86 +++ tests/cases/CVE-2019-19377.patch | 225 +++++++ tests/cases/CVE-2019-19448.patch | 63 ++ tests/cases/CVE-2019-19449.patch | 67 ++ tests/cases/CVE-2019-19816.patch | 221 +++++++ tests/cases/CVE-2019-25044.patch | 204 ++++++ tests/cases/CVE-2019-25045.patch | 133 ++++ tests/cases/CVE-2019-6974.patch | 55 ++ tests/cases/CVE-2019-8912.patch | 120 ++++ tests/cases/CVE-2019-8956.patch | 44 ++ tests/cases/CVE-2019-9162.patch | 48 ++ tests/cases/CVE-2019-9500.patch | 32 + tests/cases/CVE-2019-9503.patch | 104 +++ tests/cases/CVE-2020-12351.patch | 83 +++ tests/cases/CVE-2020-14381.patch | 226 +++++++ tests/cases/CVE-2020-14386.patch | 53 ++ tests/cases/CVE-2020-25221.patch | 80 +++ tests/cases/CVE-2020-25668.patch | 165 +++++ tests/cases/CVE-2020-25669.patch | 92 +++ tests/cases/CVE-2020-25705.patch | 69 ++ tests/cases/CVE-2020-27786.patch | 131 ++++ tests/cases/CVE-2020-28374.patch | 208 ++++++ tests/cases/CVE-2020-29368.patch | 101 +++ tests/cases/CVE-2020-29369.patch | 88 +++ tests/cases/CVE-2020-29370.patch | 44 ++ tests/cases/CVE-2020-29373.patch | 110 ++++ tests/cases/CVE-2020-29534.patch | 680 ++++++++++++++++++++ tests/cases/CVE-2020-29661.patch | 43 ++ tests/cases/CVE-2020-36385.patch | 177 +++++ tests/cases/CVE-2020-36386.patch | 38 ++ tests/cases/CVE-2020-36387.patch | 109 ++++ tests/cases/CVE-2020-8835.patch | 273 ++++++++ tests/cases/CVE-2021-22543.patch | 69 ++ tests/cases/CVE-2021-22555.patch | 65 ++ tests/cases/CVE-2021-22600.patch | 40 ++ tests/cases/CVE-2021-23134.patch | 72 +++ tests/cases/CVE-2021-26708.patch | 106 +++ tests/cases/CVE-2021-27364.patch | 46 ++ tests/cases/CVE-2021-27365.patch | 48 ++ tests/cases/CVE-2021-29657.patch | 65 ++ tests/cases/CVE-2021-33033.patch | 137 ++++ tests/cases/CVE-2021-33909.patch | 34 + tests/cases/CVE-2021-34866.patch | 53 ++ tests/cases/CVE-2021-3490.patch | 111 ++++ tests/cases/CVE-2021-3656.patch | 38 ++ tests/cases/CVE-2021-3715.patch | 44 ++ tests/cases/CVE-2021-37576.patch | 74 +++ tests/cases/CVE-2021-3760.patch | 33 + tests/cases/CVE-2021-38300.patch | 265 ++++++++ tests/cases/CVE-2021-40490.patch | 42 ++ tests/cases/CVE-2021-41073.patch | 44 ++ tests/cases/CVE-2021-4154.patch | 63 ++ tests/cases/CVE-2021-42008.patch | 61 ++ tests/cases/CVE-2021-43267.patch | 92 +++ tests/cases/CVE-2021-44733.patch | 346 ++++++++++ tests/cases/CVE-2022-0185.patch | 37 ++ tests/cases/CVE-2022-0435.patch | 85 +++ tests/cases/CVE-2022-0492.patch | 53 ++ tests/cases/CVE-2022-0847.patch | 42 ++ tests/cases/CVE-2022-0995.patch | 102 +++ tests/cases/CVE-2022-1011.patch | 82 +++ tests/cases/CVE-2022-1015.patch | 70 ++ tests/cases/CVE-2022-1679.patch | 88 +++ tests/cases/CVE-2022-24122.patch | 56 ++ tests/cases/CVE-2022-25636.patch | 148 +++++ tests/cases/CVE-2022-2585.patch | 46 ++ tests/cases/CVE-2022-2602.patch | 102 +++ tests/cases/CVE-2022-2639.patch | 82 +++ tests/cases/CVE-2022-27666.patch | 86 +++ tests/cases/CVE-2022-29582.patch | 55 ++ tests/cases/CVE-2022-29968.patch | 29 + tests/cases/CVE-2022-32250.patch | 98 +++ tests/cases/CVE-2022-34918.patch | 43 ++ tests/cases/CVE-2022-3640.patch | 135 ++++ tests/cases/CVE-2022-36946.patch | 47 ++ tests/cases/CVE-2022-3910.patch | 32 + tests/cases/CVE-2022-41674.patch | 46 ++ tests/cases/CVE-2022-42719.patch | 103 +++ tests/cases/CVE-2022-42720.patch | 92 +++ tests/cases/CVE-2023-0045.patch | 30 + tests/cases/CVE-2023-0122.patch | 31 + tests/cases/CVE-2023-0179.patch | 37 ++ tests/cases/CVE-2023-0210.patch | 41 ++ tests/cases/CVE-2023-0386.patch | 47 ++ tests/cases/CVE-2023-0461.patch | 78 +++ tests/cases/CVE-2023-1829.patch | 1036 ++++++++++++++++++++++++++++++ tests/cases/CVE-2023-2156.patch | 189 ++++++ tests/cases/CVE-2023-2163.patch | 419 ++++++++++++ tests/cases/CVE-2023-31248.patch | 120 ++++ tests/cases/CVE-2023-31436.patch | 128 ++++ tests/cases/CVE-2023-32233.patch | 120 ++++ tests/cases/CVE-2023-3269.patch | 302 +++++++++ tests/cases/CVE-2023-3389.patch | 44 ++ tests/cases/CVE-2023-3390.patch | 70 ++ tests/cases/CVE-2023-35001.patch | 210 ++++++ tests/cases/CVE-2023-3609.patch | 72 +++ tests/cases/CVE-2023-3611.patch | 90 +++ tests/cases/CVE-2023-3776.patch | 56 ++ tests/cases/CVE-2023-3812.patch | 90 +++ tests/cases/CVE-2023-4004.patch | 57 ++ tests/cases/CVE-2023-42753.patch | 36 ++ tests/cases/CVE-2023-4622.patch | 136 ++++ tests/cases/CVE-2023-5345.patch | 30 + tests/cases/CVE-2023-6111.patch | 75 +++ 130 files changed, 13269 insertions(+), 18 deletions(-) create mode 100644 tests/bench_parse.py create mode 100644 tests/cases/CVE-2017-11176.patch create mode 100644 tests/cases/CVE-2017-16995.patch create mode 100644 tests/cases/CVE-2017-18509.patch create mode 100644 tests/cases/CVE-2017-5123.patch create mode 100644 tests/cases/CVE-2018-25015.patch create mode 100644 tests/cases/CVE-2019-10639.patch create mode 100644 tests/cases/CVE-2019-11477.patch create mode 100644 tests/cases/CVE-2019-11599.patch create mode 100644 tests/cases/CVE-2019-11815.patch create mode 100644 tests/cases/CVE-2019-13233.patch create mode 100644 tests/cases/CVE-2019-13272.patch create mode 100644 tests/cases/CVE-2019-15239.patch create mode 100644 tests/cases/CVE-2019-18198.patch create mode 100644 tests/cases/CVE-2019-18675.patch create mode 100644 tests/cases/CVE-2019-18683.patch create mode 100644 tests/cases/CVE-2019-18805.patch create mode 100644 tests/cases/CVE-2019-19241.patch create mode 100644 tests/cases/CVE-2019-19377.patch create mode 100644 tests/cases/CVE-2019-19448.patch create mode 100644 tests/cases/CVE-2019-19449.patch create mode 100644 tests/cases/CVE-2019-19816.patch create mode 100644 tests/cases/CVE-2019-25044.patch create mode 100644 tests/cases/CVE-2019-25045.patch create mode 100644 tests/cases/CVE-2019-6974.patch create mode 100644 tests/cases/CVE-2019-8912.patch create mode 100644 tests/cases/CVE-2019-8956.patch create mode 100644 tests/cases/CVE-2019-9162.patch create mode 100644 tests/cases/CVE-2019-9500.patch create mode 100644 tests/cases/CVE-2019-9503.patch create mode 100644 tests/cases/CVE-2020-12351.patch create mode 100644 tests/cases/CVE-2020-14381.patch create mode 100644 tests/cases/CVE-2020-14386.patch create mode 100644 tests/cases/CVE-2020-25221.patch create mode 100644 tests/cases/CVE-2020-25668.patch create mode 100644 tests/cases/CVE-2020-25669.patch create mode 100644 tests/cases/CVE-2020-25705.patch create mode 100644 tests/cases/CVE-2020-27786.patch create mode 100644 tests/cases/CVE-2020-28374.patch create mode 100644 tests/cases/CVE-2020-29368.patch create mode 100644 tests/cases/CVE-2020-29369.patch create mode 100644 tests/cases/CVE-2020-29370.patch create mode 100644 tests/cases/CVE-2020-29373.patch create mode 100644 tests/cases/CVE-2020-29534.patch create mode 100644 tests/cases/CVE-2020-29661.patch create mode 100644 tests/cases/CVE-2020-36385.patch create mode 100644 tests/cases/CVE-2020-36386.patch create mode 100644 tests/cases/CVE-2020-36387.patch create mode 100644 tests/cases/CVE-2020-8835.patch create mode 100644 tests/cases/CVE-2021-22543.patch create mode 100644 tests/cases/CVE-2021-22555.patch create mode 100644 tests/cases/CVE-2021-22600.patch create mode 100644 tests/cases/CVE-2021-23134.patch create mode 100644 tests/cases/CVE-2021-26708.patch create mode 100644 tests/cases/CVE-2021-27364.patch create mode 100644 tests/cases/CVE-2021-27365.patch create mode 100644 tests/cases/CVE-2021-29657.patch create mode 100644 tests/cases/CVE-2021-33033.patch create mode 100644 tests/cases/CVE-2021-33909.patch create mode 100644 tests/cases/CVE-2021-34866.patch create mode 100644 tests/cases/CVE-2021-3490.patch create mode 100644 tests/cases/CVE-2021-3656.patch create mode 100644 tests/cases/CVE-2021-3715.patch create mode 100644 tests/cases/CVE-2021-37576.patch create mode 100644 tests/cases/CVE-2021-3760.patch create mode 100644 tests/cases/CVE-2021-38300.patch create mode 100644 tests/cases/CVE-2021-40490.patch create mode 100644 tests/cases/CVE-2021-41073.patch create mode 100644 tests/cases/CVE-2021-4154.patch create mode 100644 tests/cases/CVE-2021-42008.patch create mode 100644 tests/cases/CVE-2021-43267.patch create mode 100644 tests/cases/CVE-2021-44733.patch create mode 100644 tests/cases/CVE-2022-0185.patch create mode 100644 tests/cases/CVE-2022-0435.patch create mode 100644 tests/cases/CVE-2022-0492.patch create mode 100644 tests/cases/CVE-2022-0847.patch create mode 100644 tests/cases/CVE-2022-0995.patch create mode 100644 tests/cases/CVE-2022-1011.patch create mode 100644 tests/cases/CVE-2022-1015.patch create mode 100644 tests/cases/CVE-2022-1679.patch create mode 100644 tests/cases/CVE-2022-24122.patch create mode 100644 tests/cases/CVE-2022-25636.patch create mode 100644 tests/cases/CVE-2022-2585.patch create mode 100644 tests/cases/CVE-2022-2602.patch create mode 100644 tests/cases/CVE-2022-2639.patch create mode 100644 tests/cases/CVE-2022-27666.patch create mode 100644 tests/cases/CVE-2022-29582.patch create mode 100644 tests/cases/CVE-2022-29968.patch create mode 100644 tests/cases/CVE-2022-32250.patch create mode 100644 tests/cases/CVE-2022-34918.patch create mode 100644 tests/cases/CVE-2022-3640.patch create mode 100644 tests/cases/CVE-2022-36946.patch create mode 100644 tests/cases/CVE-2022-3910.patch create mode 100644 tests/cases/CVE-2022-41674.patch create mode 100644 tests/cases/CVE-2022-42719.patch create mode 100644 tests/cases/CVE-2022-42720.patch create mode 100644 tests/cases/CVE-2023-0045.patch create mode 100644 tests/cases/CVE-2023-0122.patch create mode 100644 tests/cases/CVE-2023-0179.patch create mode 100644 tests/cases/CVE-2023-0210.patch create mode 100644 tests/cases/CVE-2023-0386.patch create mode 100644 tests/cases/CVE-2023-0461.patch create mode 100644 tests/cases/CVE-2023-1829.patch create mode 100644 tests/cases/CVE-2023-2156.patch create mode 100644 tests/cases/CVE-2023-2163.patch create mode 100644 tests/cases/CVE-2023-31248.patch create mode 100644 tests/cases/CVE-2023-31436.patch create mode 100644 tests/cases/CVE-2023-32233.patch create mode 100644 tests/cases/CVE-2023-3269.patch create mode 100644 tests/cases/CVE-2023-3389.patch create mode 100644 tests/cases/CVE-2023-3390.patch create mode 100644 tests/cases/CVE-2023-35001.patch create mode 100644 tests/cases/CVE-2023-3609.patch create mode 100644 tests/cases/CVE-2023-3611.patch create mode 100644 tests/cases/CVE-2023-3776.patch create mode 100644 tests/cases/CVE-2023-3812.patch create mode 100644 tests/cases/CVE-2023-4004.patch create mode 100644 tests/cases/CVE-2023-42753.patch create mode 100644 tests/cases/CVE-2023-4622.patch create mode 100644 tests/cases/CVE-2023-5345.patch create mode 100644 tests/cases/CVE-2023-6111.patch diff --git a/.gitignore b/.gitignore index 267b4b1..f1ad217 100644 --- a/.gitignore +++ b/.gitignore @@ -162,7 +162,6 @@ cython_debug/ kernel/ patches/ -*.patch .pdm-python .vscode diff --git a/README.md b/README.md index f0ca838..14343e9 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,36 @@ Modern patch, written in Python. ## Usage +The following commands are supported: + +### apply + +Apply a patch to target files. + ```shell patche apply ``` +Options: +- `-R, --reverse`: Assume patches were created with old and new files swapped +- `-F, --fuzz LINES`: Set the fuzz factor to LINES for inexact matching + +### show + +Show details of a patch file. + +```shell +patche show +``` + +### settings + +Display current configuration. + +```shell +patche settings +``` + ## Config `patche` loads the configuration from a file named `.patche.env` in `$HOME`. diff --git a/pdm.lock b/pdm.lock index 43cc7c4..4077679 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:1eb17fcfa8df20c286575f2ef8ebaf0636e0be53b8c68f842521355a0011c399" +content_hash = "sha256:f2ff73482cce9eb1ea8a0f79d067bbfe094df17148817619fe9ac31c47bde566" [[metadata.targets]] requires_python = ">=3.10" @@ -335,13 +335,24 @@ files = [ {file = "viztracer-1.0.0.tar.gz", hash = "sha256:8377376fb255ee1543a2ce97bc65c68c26238d754aff9c4a049bb05948ece53b"}, ] +[[package]] +name = "whatthepatch" +version = "1.0.7" +requires_python = ">=3.9" +summary = "A patch parsing and application library." +groups = ["dev"] +files = [ + {file = "whatthepatch-1.0.7-py3-none-any.whl", hash = "sha256:1b6f655fd31091c001c209529dfaabbabdbad438f5de14e3951266ea0fc6e7ed"}, + {file = "whatthepatch-1.0.7.tar.gz", hash = "sha256:9eefb4ebea5200408e02d413d2b4bc28daea6b78bb4b4d53431af7245f7d7edf"}, +] + [[package]] name = "whatthepatch-pydantic" -version = "1.0.6a2" +version = "1.0.6a3" requires_python = ">=3.7" summary = "A patch parsing and application library." groups = ["default"] files = [ - {file = "whatthepatch_pydantic-1.0.6a2-py3-none-any.whl", hash = "sha256:d767a6e149d36ea07785eb56363a4b033c19b944a24dc8bcd3c0af6beb0744b9"}, - {file = "whatthepatch_pydantic-1.0.6a2.tar.gz", hash = "sha256:a929f39579fe45bbeb7a4c599e1bd2ab93b9e947f2cfa02ddd82928469785880"}, + {file = "whatthepatch_pydantic-1.0.6a3-py3-none-any.whl", hash = "sha256:2e4d5fc24ad904ab2b2ab8e657948987fa464fa7f5031fdf5da37ae488c90643"}, + {file = "whatthepatch_pydantic-1.0.6a3.tar.gz", hash = "sha256:d47ecfb272bca87f45b3f0e6058616a7f4bb1b81d6a7d5caa3ffaf51c2b89dfc"}, ] diff --git a/pyproject.toml b/pyproject.toml index 6fafca5..23792e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ dependencies = [ "typer[all]>=0.9.0", "pydantic>=2.5.3", "pydantic-settings>=2.2.1", - "whatthepatch-pydantic==1.0.6a2", + "whatthepatch-pydantic==1.0.6a3", ] requires-python = ">=3.10" readme = "README.md" @@ -28,4 +28,5 @@ distribution = true [tool.pdm.dev-dependencies] dev = [ "viztracer>=0.16.3", + "whatthepatch>=1.0.7", ] diff --git a/requirements.txt b/requirements.txt index 3c403c4..aafb18d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,4 +17,5 @@ shellingham==1.5.4 typer[all]==0.15.1 typing-extensions==4.12.2 viztracer==1.0.0 -whatthepatch-pydantic==1.0.6a2 +whatthepatch==1.0.7 +whatthepatch-pydantic==1.0.6a3 diff --git a/src/Patche/commands/apply.py b/src/Patche/commands/apply.py index d0eab23..1d07a1b 100644 --- a/src/Patche/commands/apply.py +++ b/src/Patche/commands/apply.py @@ -11,10 +11,21 @@ @app.command() def apply( - # filename: str, - patch_path: str, - reverse: Annotated[bool, typer.Option("-R", "--reverse")] = False, - fuzz: Annotated[int, typer.Option("-F", "--fuzz")] = 0, + patch_path: Annotated[str, typer.Argument(help="Path to the patch file")], + reverse: Annotated[ + bool, + typer.Option( + "-R", + "--reverse", + help="Assume patches were created with old and new files swapped.", + ), + ], + fuzz: Annotated[ + int, + typer.Option( + "-F", "--fuzz", help="Set the fuzz factor to LINES for inexact matching." + ), + ] = 0, ): """ Apply a patch to a file. diff --git a/src/Patche/commands/show.py b/src/Patche/commands/show.py index a1cc0f9..61e5280 100644 --- a/src/Patche/commands/show.py +++ b/src/Patche/commands/show.py @@ -1,5 +1,7 @@ import os +from typing import Annotated +import typer from rich.console import Console from rich.table import Table @@ -8,9 +10,9 @@ @app.command() -def show(filename: str): +def show(filename: Annotated[str, typer.Argument(help="Path to the patch file")]): """ - Show detail of a patch file. + Show details of a patch file. """ if not os.path.exists(filename): logger.error(f"Warning: {filename} not found!") diff --git a/src/Patche/model.py b/src/Patche/model.py index 8f8891e..9ee73b3 100644 --- a/src/Patche/model.py +++ b/src/Patche/model.py @@ -2,9 +2,9 @@ from typing import Optional from pydantic import BaseModel -from whatthepatch.model import Change -from whatthepatch.model import Diff as WTPDiff -from whatthepatch.model import Header +from whatthepatch_pydantic.model import Change +from whatthepatch_pydantic.model import Diff as WTPDiff +from whatthepatch_pydantic.model import Header class Line(BaseModel): diff --git a/src/Patche/utils/parse.py b/src/Patche/utils/parse.py index 92fd2e8..1af4232 100644 --- a/src/Patche/utils/parse.py +++ b/src/Patche/utils/parse.py @@ -1,7 +1,7 @@ import re -from whatthepatch import parse_patch as wtp_parse_patch -from whatthepatch.model import Diff as WTPDiff +from whatthepatch_pydantic import parse_patch as wtp_parse_patch +from whatthepatch_pydantic.model import Diff as WTPDiff from Patche.config import settings from Patche.model import Change, Diff, Hunk, Patch diff --git a/tests/bench_parse.py b/tests/bench_parse.py new file mode 100644 index 0000000..7b9b72e --- /dev/null +++ b/tests/bench_parse.py @@ -0,0 +1,126 @@ +import logging +import os +import time + +from rich.console import Console +from rich.logging import RichHandler +from rich.table import Table + +logging.basicConfig( + level="INFO", + format="%(message)s", + datefmt="[%X]", + handlers=[RichHandler(markup=True)], +) + +logger = logging.getLogger(__name__) + + +def bench_parse(): + + # 创建表格 + table = Table(title="Benchmark Results") + table.add_column("File Name", style="cyan") + table.add_column("parse_patch Time", style="green") + table.add_column("parse_patch_patche Time", style="green") + table.add_column("parse_patch_pydantic Time", style="green") + table.add_column("Time Difference patche", style="yellow") + table.add_column("Percentage", style="red") + table.add_column("Time Difference pydantic", style="yellow") + table.add_column("Percentage", style="red") + + from whatthepatch import parse_patch + from whatthepatch.patch import diffobj + from whatthepatch_pydantic import parse_patch as parse_patch_pydantic + from whatthepatch_pydantic.patch import Diff as Diff_pydantic + + from Patche.model import Diff + from Patche.utils.parse import parse_patch as parse_patch_patche + + time_differences = [] + time_differences_pydantic = [] + + time_diffes = [] + time_diffes_patche = [] + time_diffes_pydantic = [] + + percentage_diffs_patche = [] + percentage_diffs_pydantic = [] + + for patch_file in os.listdir("tests/cases"): + with open(f"tests/cases/{patch_file}", "r") as f: + patch = f.read() + + try: + st = time.time() + diffes: list[diffobj] = [] + for diff in parse_patch(patch): + diffes.append(diff) + parse_time = time.time() - st + + st = time.time() + diffes_patche: list[Diff] = [] + for diff in parse_patch_patche(patch).diff: + diffes_patche.append(diff) + parse_patche_time = time.time() - st + + st = time.time() + diffes_pydantic: list[Diff_pydantic] = [] + for diff in parse_patch_pydantic(patch): + diffes_pydantic.append(diff) + parse_pydantic_time = time.time() - st + + time_diff = parse_patche_time - parse_time + percentage = (parse_patche_time / parse_time - 1) * 100 + + time_diff_pydantic = parse_pydantic_time - parse_time + percentage_pydantic = (parse_pydantic_time / parse_time - 1) * 100 + + # 添加数据到表格 + table.add_row( + patch_file, + f"{parse_time:.8f}s", + f"{parse_patche_time:.8f}s", + f"{parse_pydantic_time:.8f}s", + f"{time_diff:.8f}s", + f"{percentage:+.2f}%", + f"{time_diff_pydantic:.8f}s", + f"{percentage_pydantic:+.2f}%", + ) + + time_differences.append(time_diff) + time_differences_pydantic.append(time_diff_pydantic) + + percentage_diffs_patche.append(percentage) + percentage_diffs_pydantic.append(percentage_pydantic) + + time_diffes.append(parse_time) + time_diffes_patche.append(parse_patche_time) + time_diffes_pydantic.append(parse_pydantic_time) + + assert len(diffes) == len(diffes_patche) + for d1, d2 in zip(diffes, diffes_patche): + assert len(d1.changes) == len(d2.changes) + + except Exception as e: + logger.error(f"Error in {patch_file}") + + # 添加统计信息到表格 + table.add_row( + "Average", + f"{sum(time_diffes)/len(time_diffes):.8f}s", + f"{sum(time_diffes_patche)/len(time_diffes_patche):.8f}s", + f"{sum(time_diffes_pydantic)/len(time_diffes_pydantic):.8f}s", + f"{sum(time_differences) / len(time_differences):.8f}s", + f"{sum(percentage_diffs_patche) / len(percentage_diffs_patche):+.2f}%", + f"{sum(time_differences_pydantic)/len(time_differences_pydantic):.8f}s", + f"{sum(percentage_diffs_pydantic)/len(percentage_diffs_pydantic):+.2f}%", + ) + + # 显示表格 + console = Console() + console.print(table) + + +if __name__ == "__main__": + bench_parse() diff --git a/tests/cases/CVE-2017-11176.patch b/tests/cases/CVE-2017-11176.patch new file mode 100644 index 0000000..d9c4b09 --- /dev/null +++ b/tests/cases/CVE-2017-11176.patch @@ -0,0 +1,48 @@ +From f991af3daabaecff34684fd51fac80319d1baad1 Mon Sep 17 00:00:00 2001 +From: Cong Wang +Date: Sun, 9 Jul 2017 13:19:55 -0700 +Subject: mqueue: fix a use-after-free in sys_mq_notify() + +The retry logic for netlink_attachskb() inside sys_mq_notify() +is nasty and vulnerable: + +1) The sock refcnt is already released when retry is needed +2) The fd is controllable by user-space because we already + release the file refcnt + +so we when retry but the fd has been just closed by user-space +during this small window, we end up calling netlink_detachskb() +on the error path which releases the sock again, later when +the user-space closes this socket a use-after-free could be +triggered. + +Setting 'sock' to NULL here should be sufficient to fix it. + +Reported-by: GeneBlue +Signed-off-by: Cong Wang +Cc: Andrew Morton +Cc: Manfred Spraul +Cc: stable@kernel.org +Signed-off-by: Linus Torvalds +--- + ipc/mqueue.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/ipc/mqueue.c b/ipc/mqueue.c +index c9ff943f19abc..eb1391b52c6f8 100644 +--- a/ipc/mqueue.c ++++ b/ipc/mqueue.c +@@ -1270,8 +1270,10 @@ retry: + + timeo = MAX_SCHEDULE_TIMEOUT; + ret = netlink_attachskb(sock, nc, &timeo, NULL); +- if (ret == 1) ++ if (ret == 1) { ++ sock = NULL; + goto retry; ++ } + if (ret) { + sock = NULL; + nc = NULL; +-- +cgit diff --git a/tests/cases/CVE-2017-16995.patch b/tests/cases/CVE-2017-16995.patch new file mode 100644 index 0000000..d0fc688 --- /dev/null +++ b/tests/cases/CVE-2017-16995.patch @@ -0,0 +1,48 @@ +From 95a762e2c8c942780948091f8f2a4f32fce1ac6f Mon Sep 17 00:00:00 2001 +From: Jann Horn +Date: Mon, 18 Dec 2017 20:11:54 -0800 +Subject: bpf: fix incorrect sign extension in check_alu_op() + +Distinguish between +BPF_ALU64|BPF_MOV|BPF_K (load 32-bit immediate, sign-extended to 64-bit) +and BPF_ALU|BPF_MOV|BPF_K (load 32-bit immediate, zero-padded to 64-bit); +only perform sign extension in the first case. + +Starting with v4.14, this is exploitable by unprivileged users as long as +the unprivileged_bpf_disabled sysctl isn't set. + +Debian assigned CVE-2017-16995 for this issue. + +v3: + - add CVE number (Ben Hutchings) + +Fixes: 484611357c19 ("bpf: allow access into map value arrays") +Signed-off-by: Jann Horn +Acked-by: Edward Cree +Signed-off-by: Alexei Starovoitov +Signed-off-by: Daniel Borkmann +--- + kernel/bpf/verifier.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c +index 625e358ca765e..c086010ae51ed 100644 +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -2408,7 +2408,13 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) + * remember the value we stored into this reg + */ + regs[insn->dst_reg].type = SCALAR_VALUE; +- __mark_reg_known(regs + insn->dst_reg, insn->imm); ++ if (BPF_CLASS(insn->code) == BPF_ALU64) { ++ __mark_reg_known(regs + insn->dst_reg, ++ insn->imm); ++ } else { ++ __mark_reg_known(regs + insn->dst_reg, ++ (u32)insn->imm); ++ } + } + + } else if (opcode > BPF_END) { +-- +cgit diff --git a/tests/cases/CVE-2017-18509.patch b/tests/cases/CVE-2017-18509.patch new file mode 100644 index 0000000..4b4d45e --- /dev/null +++ b/tests/cases/CVE-2017-18509.patch @@ -0,0 +1,62 @@ +From 99253eb750fda6a644d5188fb26c43bad8d5a745 Mon Sep 17 00:00:00 2001 +From: Xin Long +Date: Fri, 24 Feb 2017 16:29:06 +0800 +Subject: ipv6: check sk sk_type and protocol early in ip_mroute_set/getsockopt + +Commit 5e1859fbcc3c ("ipv4: ipmr: various fixes and cleanups") fixed +the issue for ipv4 ipmr: + + ip_mroute_setsockopt() & ip_mroute_getsockopt() should not + access/set raw_sk(sk)->ipmr_table before making sure the socket + is a raw socket, and protocol is IGMP + +The same fix should be done for ipv6 ipmr as well. + +This patch can fix the panic caused by overwriting the same offset +as ipmr_table as in raw_sk(sk) when accessing other type's socket +by ip_mroute_setsockopt(). + +Signed-off-by: Xin Long +Signed-off-by: David S. Miller +--- + net/ipv6/ip6mr.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c +index babaf3ec2742b..6ba6c900ebcf4 100644 +--- a/net/ipv6/ip6mr.c ++++ b/net/ipv6/ip6mr.c +@@ -1666,6 +1666,10 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns + struct net *net = sock_net(sk); + struct mr6_table *mrt; + ++ if (sk->sk_type != SOCK_RAW || ++ inet_sk(sk)->inet_num != IPPROTO_ICMPV6) ++ return -EOPNOTSUPP; ++ + mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT); + if (!mrt) + return -ENOENT; +@@ -1677,9 +1681,6 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns + + switch (optname) { + case MRT6_INIT: +- if (sk->sk_type != SOCK_RAW || +- inet_sk(sk)->inet_num != IPPROTO_ICMPV6) +- return -EOPNOTSUPP; + if (optlen < sizeof(int)) + return -EINVAL; + +@@ -1815,6 +1816,10 @@ int ip6_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, + struct net *net = sock_net(sk); + struct mr6_table *mrt; + ++ if (sk->sk_type != SOCK_RAW || ++ inet_sk(sk)->inet_num != IPPROTO_ICMPV6) ++ return -EOPNOTSUPP; ++ + mrt = ip6mr_get_table(net, raw6_sk(sk)->ip6mr_table ? : RT6_TABLE_DFLT); + if (!mrt) + return -ENOENT; +-- +cgit diff --git a/tests/cases/CVE-2017-5123.patch b/tests/cases/CVE-2017-5123.patch new file mode 100644 index 0000000..132391b --- /dev/null +++ b/tests/cases/CVE-2017-5123.patch @@ -0,0 +1,45 @@ +From 96ca579a1ecc943b75beba58bebb0356f6cc4b51 Mon Sep 17 00:00:00 2001 +From: Kees Cook +Date: Mon, 9 Oct 2017 11:36:52 -0700 +Subject: waitid(): Add missing access_ok() checks + +Adds missing access_ok() checks. + +CVE-2017-5123 + +Reported-by: Chris Salls +Signed-off-by: Kees Cook +Acked-by: Al Viro +Fixes: 4c48abe91be0 ("waitid(): switch copyout of siginfo to unsafe_put_user()") +Cc: stable@kernel.org # 4.13 +Signed-off-by: Linus Torvalds +--- + kernel/exit.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/kernel/exit.c b/kernel/exit.c +index f2cd53e92147c..cf28528842bcf 100644 +--- a/kernel/exit.c ++++ b/kernel/exit.c +@@ -1610,6 +1610,9 @@ SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *, + if (!infop) + return err; + ++ if (!access_ok(VERIFY_WRITE, infop, sizeof(*infop))) ++ goto Efault; ++ + user_access_begin(); + unsafe_put_user(signo, &infop->si_signo, Efault); + unsafe_put_user(0, &infop->si_errno, Efault); +@@ -1735,6 +1738,9 @@ COMPAT_SYSCALL_DEFINE5(waitid, + if (!infop) + return err; + ++ if (!access_ok(VERIFY_WRITE, infop, sizeof(*infop))) ++ goto Efault; ++ + user_access_begin(); + unsafe_put_user(signo, &infop->si_signo, Efault); + unsafe_put_user(0, &infop->si_errno, Efault); +-- +cgit diff --git a/tests/cases/CVE-2018-25015.patch b/tests/cases/CVE-2018-25015.patch new file mode 100644 index 0000000..49f4720 --- /dev/null +++ b/tests/cases/CVE-2018-25015.patch @@ -0,0 +1,86 @@ +From a0ff660058b88d12625a783ce9e5c1371c87951f Mon Sep 17 00:00:00 2001 +From: Xin Long +Date: Mon, 15 Jan 2018 17:01:36 +0800 +Subject: sctp: return error if the asoc has been peeled off in + sctp_wait_for_sndbuf + +After commit cea0cc80a677 ("sctp: use the right sk after waking up from +wait_buf sleep"), it may change to lock another sk if the asoc has been +peeled off in sctp_wait_for_sndbuf. + +However, the asoc's new sk could be already closed elsewhere, as it's in +the sendmsg context of the old sk that can't avoid the new sk's closing. +If the sk's last one refcnt is held by this asoc, later on after putting +this asoc, the new sk will be freed, while under it's own lock. + +This patch is to revert that commit, but fix the old issue by returning +error under the old sk's lock. + +Fixes: cea0cc80a677 ("sctp: use the right sk after waking up from wait_buf sleep") +Reported-by: syzbot+ac6ea7baa4432811eb50@syzkaller.appspotmail.com +Signed-off-by: Xin Long +Acked-by: Neil Horman +Signed-off-by: David S. Miller +--- + net/sctp/socket.c | 16 ++++++---------- + 1 file changed, 6 insertions(+), 10 deletions(-) + +diff --git a/net/sctp/socket.c b/net/sctp/socket.c +index 15ae018b386fb..feb2ca69827a7 100644 +--- a/net/sctp/socket.c ++++ b/net/sctp/socket.c +@@ -85,7 +85,7 @@ + static int sctp_writeable(struct sock *sk); + static void sctp_wfree(struct sk_buff *skb); + static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, +- size_t msg_len, struct sock **orig_sk); ++ size_t msg_len); + static int sctp_wait_for_packet(struct sock *sk, int *err, long *timeo_p); + static int sctp_wait_for_connect(struct sctp_association *, long *timeo_p); + static int sctp_wait_for_accept(struct sock *sk, long timeo); +@@ -1977,7 +1977,7 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len) + timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); + if (!sctp_wspace(asoc)) { + /* sk can be changed by peel off when waiting for buf. */ +- err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len, &sk); ++ err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len); + if (err) { + if (err == -ESRCH) { + /* asoc is already dead. */ +@@ -8022,12 +8022,12 @@ void sctp_sock_rfree(struct sk_buff *skb) + + /* Helper function to wait for space in the sndbuf. */ + static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, +- size_t msg_len, struct sock **orig_sk) ++ size_t msg_len) + { + struct sock *sk = asoc->base.sk; +- int err = 0; + long current_timeo = *timeo_p; + DEFINE_WAIT(wait); ++ int err = 0; + + pr_debug("%s: asoc:%p, timeo:%ld, msg_len:%zu\n", __func__, asoc, + *timeo_p, msg_len); +@@ -8056,17 +8056,13 @@ static int sctp_wait_for_sndbuf(struct sctp_association *asoc, long *timeo_p, + release_sock(sk); + current_timeo = schedule_timeout(current_timeo); + lock_sock(sk); +- if (sk != asoc->base.sk) { +- release_sock(sk); +- sk = asoc->base.sk; +- lock_sock(sk); +- } ++ if (sk != asoc->base.sk) ++ goto do_error; + + *timeo_p = current_timeo; + } + + out: +- *orig_sk = sk; + finish_wait(&asoc->wait, &wait); + + /* Release the association's refcnt. */ +-- +cgit diff --git a/tests/cases/CVE-2019-10639.patch b/tests/cases/CVE-2019-10639.patch new file mode 100644 index 0000000..cb7b9f6 --- /dev/null +++ b/tests/cases/CVE-2019-10639.patch @@ -0,0 +1,77 @@ +From 355b98553789b646ed97ad801a619ff898471b92 Mon Sep 17 00:00:00 2001 +From: Eric Dumazet +Date: Wed, 27 Mar 2019 08:21:30 -0700 +Subject: netns: provide pure entropy for net_hash_mix() + +net_hash_mix() currently uses kernel address of a struct net, +and is used in many places that could be used to reveal this +address to a patient attacker, thus defeating KASLR, for +the typical case (initial net namespace, &init_net is +not dynamically allocated) + +I believe the original implementation tried to avoid spending +too many cycles in this function, but security comes first. + +Also provide entropy regardless of CONFIG_NET_NS. + +Fixes: 0b4419162aa6 ("netns: introduce the net_hash_mix "salt" for hashes") +Signed-off-by: Eric Dumazet +Reported-by: Amit Klein +Reported-by: Benny Pinkas +Cc: Pavel Emelyanov +Signed-off-by: David S. Miller +--- + include/net/net_namespace.h | 1 + + include/net/netns/hash.h | 10 ++-------- + net/core/net_namespace.c | 1 + + 3 files changed, 4 insertions(+), 8 deletions(-) + +diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h +index a68ced28d8f47..12689ddfc24c4 100644 +--- a/include/net/net_namespace.h ++++ b/include/net/net_namespace.h +@@ -59,6 +59,7 @@ struct net { + */ + spinlock_t rules_mod_lock; + ++ u32 hash_mix; + atomic64_t cookie_gen; + + struct list_head list; /* list of network namespaces */ +diff --git a/include/net/netns/hash.h b/include/net/netns/hash.h +index 16a842456189f..d9b665151f3d9 100644 +--- a/include/net/netns/hash.h ++++ b/include/net/netns/hash.h +@@ -2,16 +2,10 @@ + #ifndef __NET_NS_HASH_H__ + #define __NET_NS_HASH_H__ + +-#include +- +-struct net; ++#include + + static inline u32 net_hash_mix(const struct net *net) + { +-#ifdef CONFIG_NET_NS +- return (u32)(((unsigned long)net) >> ilog2(sizeof(*net))); +-#else +- return 0; +-#endif ++ return net->hash_mix; + } + #endif +diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c +index 17f36317363d1..7e6dcc6257011 100644 +--- a/net/core/net_namespace.c ++++ b/net/core/net_namespace.c +@@ -304,6 +304,7 @@ static __net_init int setup_net(struct net *net, struct user_namespace *user_ns) + + refcount_set(&net->count, 1); + refcount_set(&net->passive, 1); ++ get_random_bytes(&net->hash_mix, sizeof(u32)); + net->dev_base_seq = 1; + net->user_ns = user_ns; + idr_init(&net->netns_ids); +-- +cgit diff --git a/tests/cases/CVE-2019-11477.patch b/tests/cases/CVE-2019-11477.patch new file mode 100644 index 0000000..b53bebb --- /dev/null +++ b/tests/cases/CVE-2019-11477.patch @@ -0,0 +1,166 @@ +From 3b4929f65b0d8249f19a50245cd88ed1a2f78cff Mon Sep 17 00:00:00 2001 +From: Eric Dumazet +Date: Fri, 17 May 2019 17:17:22 -0700 +Subject: tcp: limit payload size of sacked skbs + +Jonathan Looney reported that TCP can trigger the following crash +in tcp_shifted_skb() : + + BUG_ON(tcp_skb_pcount(skb) < pcount); + +This can happen if the remote peer has advertized the smallest +MSS that linux TCP accepts : 48 + +An skb can hold 17 fragments, and each fragment can hold 32KB +on x86, or 64KB on PowerPC. + +This means that the 16bit witdh of TCP_SKB_CB(skb)->tcp_gso_segs +can overflow. + +Note that tcp_sendmsg() builds skbs with less than 64KB +of payload, so this problem needs SACK to be enabled. +SACK blocks allow TCP to coalesce multiple skbs in the retransmit +queue, thus filling the 17 fragments to maximal capacity. + +CVE-2019-11477 -- u16 overflow of TCP_SKB_CB(skb)->tcp_gso_segs + +Fixes: 832d11c5cd07 ("tcp: Try to restore large SKBs while SACK processing") +Signed-off-by: Eric Dumazet +Reported-by: Jonathan Looney +Acked-by: Neal Cardwell +Reviewed-by: Tyler Hicks +Cc: Yuchung Cheng +Cc: Bruce Curtis +Cc: Jonathan Lemon +Signed-off-by: David S. Miller +--- + include/linux/tcp.h | 4 ++++ + include/net/tcp.h | 2 ++ + net/ipv4/tcp.c | 1 + + net/ipv4/tcp_input.c | 26 ++++++++++++++++++++------ + net/ipv4/tcp_output.c | 6 +++--- + 5 files changed, 30 insertions(+), 9 deletions(-) + +diff --git a/include/linux/tcp.h b/include/linux/tcp.h +index 711361af9ce01..9a478a0cd3a20 100644 +--- a/include/linux/tcp.h ++++ b/include/linux/tcp.h +@@ -484,4 +484,8 @@ static inline u16 tcp_mss_clamp(const struct tcp_sock *tp, u16 mss) + + return (user_mss && user_mss < mss) ? user_mss : mss; + } ++ ++int tcp_skb_shift(struct sk_buff *to, struct sk_buff *from, int pcount, ++ int shiftlen); ++ + #endif /* _LINUX_TCP_H */ +diff --git a/include/net/tcp.h b/include/net/tcp.h +index ac2f53fbfa6b4..582c0caa98116 100644 +--- a/include/net/tcp.h ++++ b/include/net/tcp.h +@@ -51,6 +51,8 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); + + #define MAX_TCP_HEADER (128 + MAX_HEADER) + #define MAX_TCP_OPTION_SPACE 40 ++#define TCP_MIN_SND_MSS 48 ++#define TCP_MIN_GSO_SIZE (TCP_MIN_SND_MSS - MAX_TCP_OPTION_SPACE) + + /* + * Never offer a window over 32767 without using window scaling. Some +diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c +index f12d500ec85cf..79666ef8c2e2f 100644 +--- a/net/ipv4/tcp.c ++++ b/net/ipv4/tcp.c +@@ -3868,6 +3868,7 @@ void __init tcp_init(void) + unsigned long limit; + unsigned int i; + ++ BUILD_BUG_ON(TCP_MIN_SND_MSS <= MAX_TCP_OPTION_SPACE); + BUILD_BUG_ON(sizeof(struct tcp_skb_cb) > + FIELD_SIZEOF(struct sk_buff, cb)); + +diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c +index 38dfc308c0fb9..d95ee40df6c2b 100644 +--- a/net/ipv4/tcp_input.c ++++ b/net/ipv4/tcp_input.c +@@ -1302,7 +1302,7 @@ static bool tcp_shifted_skb(struct sock *sk, struct sk_buff *prev, + TCP_SKB_CB(skb)->seq += shifted; + + tcp_skb_pcount_add(prev, pcount); +- BUG_ON(tcp_skb_pcount(skb) < pcount); ++ WARN_ON_ONCE(tcp_skb_pcount(skb) < pcount); + tcp_skb_pcount_add(skb, -pcount); + + /* When we're adding to gso_segs == 1, gso_size will be zero, +@@ -1368,6 +1368,21 @@ static int skb_can_shift(const struct sk_buff *skb) + return !skb_headlen(skb) && skb_is_nonlinear(skb); + } + ++int tcp_skb_shift(struct sk_buff *to, struct sk_buff *from, ++ int pcount, int shiftlen) ++{ ++ /* TCP min gso_size is 8 bytes (TCP_MIN_GSO_SIZE) ++ * Since TCP_SKB_CB(skb)->tcp_gso_segs is 16 bits, we need ++ * to make sure not storing more than 65535 * 8 bytes per skb, ++ * even if current MSS is bigger. ++ */ ++ if (unlikely(to->len + shiftlen >= 65535 * TCP_MIN_GSO_SIZE)) ++ return 0; ++ if (unlikely(tcp_skb_pcount(to) + pcount > 65535)) ++ return 0; ++ return skb_shift(to, from, shiftlen); ++} ++ + /* Try collapsing SACK blocks spanning across multiple skbs to a single + * skb. + */ +@@ -1473,7 +1488,7 @@ static struct sk_buff *tcp_shift_skb_data(struct sock *sk, struct sk_buff *skb, + if (!after(TCP_SKB_CB(skb)->seq + len, tp->snd_una)) + goto fallback; + +- if (!skb_shift(prev, skb, len)) ++ if (!tcp_skb_shift(prev, skb, pcount, len)) + goto fallback; + if (!tcp_shifted_skb(sk, prev, skb, state, pcount, len, mss, dup_sack)) + goto out; +@@ -1491,11 +1506,10 @@ static struct sk_buff *tcp_shift_skb_data(struct sock *sk, struct sk_buff *skb, + goto out; + + len = skb->len; +- if (skb_shift(prev, skb, len)) { +- pcount += tcp_skb_pcount(skb); +- tcp_shifted_skb(sk, prev, skb, state, tcp_skb_pcount(skb), ++ pcount = tcp_skb_pcount(skb); ++ if (tcp_skb_shift(prev, skb, pcount, len)) ++ tcp_shifted_skb(sk, prev, skb, state, pcount, + len, mss, 0); +- } + + out: + return prev; +diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c +index f429e856e2631..b8e3bbb852117 100644 +--- a/net/ipv4/tcp_output.c ++++ b/net/ipv4/tcp_output.c +@@ -1454,8 +1454,8 @@ static inline int __tcp_mtu_to_mss(struct sock *sk, int pmtu) + mss_now -= icsk->icsk_ext_hdr_len; + + /* Then reserve room for full set of TCP options and 8 bytes of data */ +- if (mss_now < 48) +- mss_now = 48; ++ if (mss_now < TCP_MIN_SND_MSS) ++ mss_now = TCP_MIN_SND_MSS; + return mss_now; + } + +@@ -2747,7 +2747,7 @@ static bool tcp_collapse_retrans(struct sock *sk, struct sk_buff *skb) + if (next_skb_size <= skb_availroom(skb)) + skb_copy_bits(next_skb, 0, skb_put(skb, next_skb_size), + next_skb_size); +- else if (!skb_shift(skb, next_skb, next_skb_size)) ++ else if (!tcp_skb_shift(skb, next_skb, 1, next_skb_size)) + return false; + } + tcp_highest_sack_replace(sk, next_skb, skb); +-- +cgit diff --git a/tests/cases/CVE-2019-11599.patch b/tests/cases/CVE-2019-11599.patch new file mode 100644 index 0000000..66e687e --- /dev/null +++ b/tests/cases/CVE-2019-11599.patch @@ -0,0 +1,242 @@ +From 04f5866e41fb70690e28397487d8bd8eea7d712a Mon Sep 17 00:00:00 2001 +From: Andrea Arcangeli +Date: Thu, 18 Apr 2019 17:50:52 -0700 +Subject: coredump: fix race condition between mmget_not_zero()/get_task_mm() + and core dumping + +The core dumping code has always run without holding the mmap_sem for +writing, despite that is the only way to ensure that the entire vma +layout will not change from under it. Only using some signal +serialization on the processes belonging to the mm is not nearly enough. +This was pointed out earlier. For example in Hugh's post from Jul 2017: + + https://lkml.kernel.org/r/alpine.LSU.2.11.1707191716030.2055@eggly.anvils + + "Not strictly relevant here, but a related note: I was very surprised + to discover, only quite recently, how handle_mm_fault() may be called + without down_read(mmap_sem) - when core dumping. That seems a + misguided optimization to me, which would also be nice to correct" + +In particular because the growsdown and growsup can move the +vm_start/vm_end the various loops the core dump does around the vma will +not be consistent if page faults can happen concurrently. + +Pretty much all users calling mmget_not_zero()/get_task_mm() and then +taking the mmap_sem had the potential to introduce unexpected side +effects in the core dumping code. + +Adding mmap_sem for writing around the ->core_dump invocation is a +viable long term fix, but it requires removing all copy user and page +faults and to replace them with get_dump_page() for all binary formats +which is not suitable as a short term fix. + +For the time being this solution manually covers the places that can +confuse the core dump either by altering the vma layout or the vma flags +while it runs. Once ->core_dump runs under mmap_sem for writing the +function mmget_still_valid() can be dropped. + +Allowing mmap_sem protected sections to run in parallel with the +coredump provides some minor parallelism advantage to the swapoff code +(which seems to be safe enough by never mangling any vma field and can +keep doing swapins in parallel to the core dumping) and to some other +corner case. + +In order to facilitate the backporting I added "Fixes: 86039bd3b4e6" +however the side effect of this same race condition in /proc/pid/mem +should be reproducible since before 2.6.12-rc2 so I couldn't add any +other "Fixes:" because there's no hash beyond the git genesis commit. + +Because find_extend_vma() is the only location outside of the process +context that could modify the "mm" structures under mmap_sem for +reading, by adding the mmget_still_valid() check to it, all other cases +that take the mmap_sem for reading don't need the new check after +mmget_not_zero()/get_task_mm(). The expand_stack() in page fault +context also doesn't need the new check, because all tasks under core +dumping are frozen. + +Link: http://lkml.kernel.org/r/20190325224949.11068-1-aarcange@redhat.com +Fixes: 86039bd3b4e6 ("userfaultfd: add new syscall to provide memory externalization") +Signed-off-by: Andrea Arcangeli +Reported-by: Jann Horn +Suggested-by: Oleg Nesterov +Acked-by: Peter Xu +Reviewed-by: Mike Rapoport +Reviewed-by: Oleg Nesterov +Reviewed-by: Jann Horn +Acked-by: Jason Gunthorpe +Acked-by: Michal Hocko +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +--- + drivers/infiniband/core/uverbs_main.c | 3 +++ + fs/proc/task_mmu.c | 18 ++++++++++++++++++ + fs/userfaultfd.c | 9 +++++++++ + include/linux/sched/mm.h | 21 +++++++++++++++++++++ + mm/mmap.c | 7 ++++++- + 5 files changed, 57 insertions(+), 1 deletion(-) + +diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c +index 70b7d80431a9b..f2e7ffe6fc546 100644 +--- a/drivers/infiniband/core/uverbs_main.c ++++ b/drivers/infiniband/core/uverbs_main.c +@@ -993,6 +993,8 @@ void uverbs_user_mmap_disassociate(struct ib_uverbs_file *ufile) + * will only be one mm, so no big deal. + */ + down_write(&mm->mmap_sem); ++ if (!mmget_still_valid(mm)) ++ goto skip_mm; + mutex_lock(&ufile->umap_lock); + list_for_each_entry_safe (priv, next_priv, &ufile->umaps, + list) { +@@ -1007,6 +1009,7 @@ void uverbs_user_mmap_disassociate(struct ib_uverbs_file *ufile) + vma->vm_flags &= ~(VM_SHARED | VM_MAYSHARE); + } + mutex_unlock(&ufile->umap_lock); ++ skip_mm: + up_write(&mm->mmap_sem); + mmput(mm); + } +diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c +index 92a91e7816d84..95ca1fe7283cf 100644 +--- a/fs/proc/task_mmu.c ++++ b/fs/proc/task_mmu.c +@@ -1143,6 +1143,24 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf, + count = -EINTR; + goto out_mm; + } ++ /* ++ * Avoid to modify vma->vm_flags ++ * without locked ops while the ++ * coredump reads the vm_flags. ++ */ ++ if (!mmget_still_valid(mm)) { ++ /* ++ * Silently return "count" ++ * like if get_task_mm() ++ * failed. FIXME: should this ++ * function have returned ++ * -ESRCH if get_task_mm() ++ * failed like if ++ * get_proc_task() fails? ++ */ ++ up_write(&mm->mmap_sem); ++ goto out_mm; ++ } + for (vma = mm->mmap; vma; vma = vma->vm_next) { + vma->vm_flags &= ~VM_SOFTDIRTY; + vma_set_page_prot(vma); +diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c +index 89800fc7dc9d5..f5de1e726356a 100644 +--- a/fs/userfaultfd.c ++++ b/fs/userfaultfd.c +@@ -629,6 +629,8 @@ static void userfaultfd_event_wait_completion(struct userfaultfd_ctx *ctx, + + /* the various vma->vm_userfaultfd_ctx still points to it */ + down_write(&mm->mmap_sem); ++ /* no task can run (and in turn coredump) yet */ ++ VM_WARN_ON(!mmget_still_valid(mm)); + for (vma = mm->mmap; vma; vma = vma->vm_next) + if (vma->vm_userfaultfd_ctx.ctx == release_new_ctx) { + vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX; +@@ -883,6 +885,8 @@ static int userfaultfd_release(struct inode *inode, struct file *file) + * taking the mmap_sem for writing. + */ + down_write(&mm->mmap_sem); ++ if (!mmget_still_valid(mm)) ++ goto skip_mm; + prev = NULL; + for (vma = mm->mmap; vma; vma = vma->vm_next) { + cond_resched(); +@@ -905,6 +909,7 @@ static int userfaultfd_release(struct inode *inode, struct file *file) + vma->vm_flags = new_flags; + vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX; + } ++skip_mm: + up_write(&mm->mmap_sem); + mmput(mm); + wakeup: +@@ -1333,6 +1338,8 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx, + goto out; + + down_write(&mm->mmap_sem); ++ if (!mmget_still_valid(mm)) ++ goto out_unlock; + vma = find_vma_prev(mm, start, &prev); + if (!vma) + goto out_unlock; +@@ -1520,6 +1527,8 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx, + goto out; + + down_write(&mm->mmap_sem); ++ if (!mmget_still_valid(mm)) ++ goto out_unlock; + vma = find_vma_prev(mm, start, &prev); + if (!vma) + goto out_unlock; +diff --git a/include/linux/sched/mm.h b/include/linux/sched/mm.h +index 0cd9f10423fb8..a3fda9f024c3c 100644 +--- a/include/linux/sched/mm.h ++++ b/include/linux/sched/mm.h +@@ -49,6 +49,27 @@ static inline void mmdrop(struct mm_struct *mm) + __mmdrop(mm); + } + ++/* ++ * This has to be called after a get_task_mm()/mmget_not_zero() ++ * followed by taking the mmap_sem for writing before modifying the ++ * vmas or anything the coredump pretends not to change from under it. ++ * ++ * NOTE: find_extend_vma() called from GUP context is the only place ++ * that can modify the "mm" (notably the vm_start/end) under mmap_sem ++ * for reading and outside the context of the process, so it is also ++ * the only case that holds the mmap_sem for reading that must call ++ * this function. Generally if the mmap_sem is hold for reading ++ * there's no need of this check after get_task_mm()/mmget_not_zero(). ++ * ++ * This function can be obsoleted and the check can be removed, after ++ * the coredump code will hold the mmap_sem for writing before ++ * invoking the ->core_dump methods. ++ */ ++static inline bool mmget_still_valid(struct mm_struct *mm) ++{ ++ return likely(!mm->core_state); ++} ++ + /** + * mmget() - Pin the address space associated with a &struct mm_struct. + * @mm: The address space to pin. +diff --git a/mm/mmap.c b/mm/mmap.c +index 41eb48d9b5276..bd7b9f293b391 100644 +--- a/mm/mmap.c ++++ b/mm/mmap.c +@@ -45,6 +45,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -2525,7 +2526,8 @@ find_extend_vma(struct mm_struct *mm, unsigned long addr) + vma = find_vma_prev(mm, addr, &prev); + if (vma && (vma->vm_start <= addr)) + return vma; +- if (!prev || expand_stack(prev, addr)) ++ /* don't alter vm_end if the coredump is running */ ++ if (!prev || !mmget_still_valid(mm) || expand_stack(prev, addr)) + return NULL; + if (prev->vm_flags & VM_LOCKED) + populate_vma_page_range(prev, addr, prev->vm_end, NULL); +@@ -2551,6 +2553,9 @@ find_extend_vma(struct mm_struct *mm, unsigned long addr) + return vma; + if (!(vma->vm_flags & VM_GROWSDOWN)) + return NULL; ++ /* don't alter vm_start if the coredump is running */ ++ if (!mmget_still_valid(mm)) ++ return NULL; + start = vma->vm_start; + if (expand_stack(vma, addr)) + return NULL; +-- +cgit diff --git a/tests/cases/CVE-2019-11815.patch b/tests/cases/CVE-2019-11815.patch new file mode 100644 index 0000000..fa4d2e2 --- /dev/null +++ b/tests/cases/CVE-2019-11815.patch @@ -0,0 +1,144 @@ +From cb66ddd156203daefb8d71158036b27b0e2caf63 Mon Sep 17 00:00:00 2001 +From: Mao Wenan +Date: Thu, 28 Mar 2019 17:10:56 +0800 +Subject: net: rds: force to destroy connection if t_sock is NULL in + rds_tcp_kill_sock(). + +When it is to cleanup net namespace, rds_tcp_exit_net() will call +rds_tcp_kill_sock(), if t_sock is NULL, it will not call +rds_conn_destroy(), rds_conn_path_destroy() and rds_tcp_conn_free() to free +connection, and the worker cp_conn_w is not stopped, afterwards the net is freed in +net_drop_ns(); While cp_conn_w rds_connect_worker() will call rds_tcp_conn_path_connect() +and reference 'net' which has already been freed. + +In rds_tcp_conn_path_connect(), rds_tcp_set_callbacks() will set t_sock = sock before +sock->ops->connect, but if connect() is failed, it will call +rds_tcp_restore_callbacks() and set t_sock = NULL, if connect is always +failed, rds_connect_worker() will try to reconnect all the time, so +rds_tcp_kill_sock() will never to cancel worker cp_conn_w and free the +connections. + +Therefore, the condition !tc->t_sock is not needed if it is going to do +cleanup_net->rds_tcp_exit_net->rds_tcp_kill_sock, because tc->t_sock is always +NULL, and there is on other path to cancel cp_conn_w and free +connection. So this patch is to fix this. + +rds_tcp_kill_sock(): +... +if (net != c_net || !tc->t_sock) +... +Acked-by: Santosh Shilimkar + +================================================================== +BUG: KASAN: use-after-free in inet_create+0xbcc/0xd28 +net/ipv4/af_inet.c:340 +Read of size 4 at addr ffff8003496a4684 by task kworker/u8:4/3721 + +CPU: 3 PID: 3721 Comm: kworker/u8:4 Not tainted 5.1.0 #11 +Hardware name: linux,dummy-virt (DT) +Workqueue: krdsd rds_connect_worker +Call trace: + dump_backtrace+0x0/0x3c0 arch/arm64/kernel/time.c:53 + show_stack+0x28/0x38 arch/arm64/kernel/traps.c:152 + __dump_stack lib/dump_stack.c:77 [inline] + dump_stack+0x120/0x188 lib/dump_stack.c:113 + print_address_description+0x68/0x278 mm/kasan/report.c:253 + kasan_report_error mm/kasan/report.c:351 [inline] + kasan_report+0x21c/0x348 mm/kasan/report.c:409 + __asan_report_load4_noabort+0x30/0x40 mm/kasan/report.c:429 + inet_create+0xbcc/0xd28 net/ipv4/af_inet.c:340 + __sock_create+0x4f8/0x770 net/socket.c:1276 + sock_create_kern+0x50/0x68 net/socket.c:1322 + rds_tcp_conn_path_connect+0x2b4/0x690 net/rds/tcp_connect.c:114 + rds_connect_worker+0x108/0x1d0 net/rds/threads.c:175 + process_one_work+0x6e8/0x1700 kernel/workqueue.c:2153 + worker_thread+0x3b0/0xdd0 kernel/workqueue.c:2296 + kthread+0x2f0/0x378 kernel/kthread.c:255 + ret_from_fork+0x10/0x18 arch/arm64/kernel/entry.S:1117 + +Allocated by task 687: + save_stack mm/kasan/kasan.c:448 [inline] + set_track mm/kasan/kasan.c:460 [inline] + kasan_kmalloc+0xd4/0x180 mm/kasan/kasan.c:553 + kasan_slab_alloc+0x14/0x20 mm/kasan/kasan.c:490 + slab_post_alloc_hook mm/slab.h:444 [inline] + slab_alloc_node mm/slub.c:2705 [inline] + slab_alloc mm/slub.c:2713 [inline] + kmem_cache_alloc+0x14c/0x388 mm/slub.c:2718 + kmem_cache_zalloc include/linux/slab.h:697 [inline] + net_alloc net/core/net_namespace.c:384 [inline] + copy_net_ns+0xc4/0x2d0 net/core/net_namespace.c:424 + create_new_namespaces+0x300/0x658 kernel/nsproxy.c:107 + unshare_nsproxy_namespaces+0xa0/0x198 kernel/nsproxy.c:206 + ksys_unshare+0x340/0x628 kernel/fork.c:2577 + __do_sys_unshare kernel/fork.c:2645 [inline] + __se_sys_unshare kernel/fork.c:2643 [inline] + __arm64_sys_unshare+0x38/0x58 kernel/fork.c:2643 + __invoke_syscall arch/arm64/kernel/syscall.c:35 [inline] + invoke_syscall arch/arm64/kernel/syscall.c:47 [inline] + el0_svc_common+0x168/0x390 arch/arm64/kernel/syscall.c:83 + el0_svc_handler+0x60/0xd0 arch/arm64/kernel/syscall.c:129 + el0_svc+0x8/0xc arch/arm64/kernel/entry.S:960 + +Freed by task 264: + save_stack mm/kasan/kasan.c:448 [inline] + set_track mm/kasan/kasan.c:460 [inline] + __kasan_slab_free+0x114/0x220 mm/kasan/kasan.c:521 + kasan_slab_free+0x10/0x18 mm/kasan/kasan.c:528 + slab_free_hook mm/slub.c:1370 [inline] + slab_free_freelist_hook mm/slub.c:1397 [inline] + slab_free mm/slub.c:2952 [inline] + kmem_cache_free+0xb8/0x3a8 mm/slub.c:2968 + net_free net/core/net_namespace.c:400 [inline] + net_drop_ns.part.6+0x78/0x90 net/core/net_namespace.c:407 + net_drop_ns net/core/net_namespace.c:406 [inline] + cleanup_net+0x53c/0x6d8 net/core/net_namespace.c:569 + process_one_work+0x6e8/0x1700 kernel/workqueue.c:2153 + worker_thread+0x3b0/0xdd0 kernel/workqueue.c:2296 + kthread+0x2f0/0x378 kernel/kthread.c:255 + ret_from_fork+0x10/0x18 arch/arm64/kernel/entry.S:1117 + +The buggy address belongs to the object at ffff8003496a3f80 + which belongs to the cache net_namespace of size 7872 +The buggy address is located 1796 bytes inside of + 7872-byte region [ffff8003496a3f80, ffff8003496a5e40) +The buggy address belongs to the page: +page:ffff7e000d25a800 count:1 mapcount:0 mapping:ffff80036ce4b000 +index:0x0 compound_mapcount: 0 +flags: 0xffffe0000008100(slab|head) +raw: 0ffffe0000008100 dead000000000100 dead000000000200 ffff80036ce4b000 +raw: 0000000000000000 0000000080040004 00000001ffffffff 0000000000000000 +page dumped because: kasan: bad access detected + +Memory state around the buggy address: + ffff8003496a4580: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb + ffff8003496a4600: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb +>ffff8003496a4680: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb + ^ + ffff8003496a4700: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb + ffff8003496a4780: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb +================================================================== + +Fixes: 467fa15356ac("RDS-TCP: Support multiple RDS-TCP listen endpoints, one per netns.") +Reported-by: Hulk Robot +Signed-off-by: Mao Wenan +Signed-off-by: David S. Miller +--- + net/rds/tcp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/rds/tcp.c b/net/rds/tcp.c +index fd26941746074..faf726e00e27c 100644 +--- a/net/rds/tcp.c ++++ b/net/rds/tcp.c +@@ -608,7 +608,7 @@ static void rds_tcp_kill_sock(struct net *net) + list_for_each_entry_safe(tc, _tc, &rds_tcp_conn_list, t_tcp_node) { + struct net *c_net = read_pnet(&tc->t_cpath->cp_conn->c_net); + +- if (net != c_net || !tc->t_sock) ++ if (net != c_net) + continue; + if (!list_has_conn(&tmp_list, tc->t_cpath->cp_conn)) { + list_move_tail(&tc->t_tcp_node, &tmp_list); +-- +cgit diff --git a/tests/cases/CVE-2019-13233.patch b/tests/cases/CVE-2019-13233.patch new file mode 100644 index 0000000..6d7a5ed --- /dev/null +++ b/tests/cases/CVE-2019-13233.patch @@ -0,0 +1,173 @@ +From de9f869616dd95e95c00bdd6b0fcd3421e8a4323 Mon Sep 17 00:00:00 2001 +From: Jann Horn +Date: Sun, 2 Jun 2019 03:15:58 +0200 +Subject: x86/insn-eval: Fix use-after-free access to LDT entry + +get_desc() computes a pointer into the LDT while holding a lock that +protects the LDT from being freed, but then drops the lock and returns the +(now potentially dangling) pointer to its caller. + +Fix it by giving the caller a copy of the LDT entry instead. + +Fixes: 670f928ba09b ("x86/insn-eval: Add utility function to get segment descriptor") +Cc: stable@vger.kernel.org +Signed-off-by: Jann Horn +Signed-off-by: Linus Torvalds +--- + arch/x86/lib/insn-eval.c | 47 ++++++++++++++++++++++++----------------------- + 1 file changed, 24 insertions(+), 23 deletions(-) + +diff --git a/arch/x86/lib/insn-eval.c b/arch/x86/lib/insn-eval.c +index cf00ab6c66210..306c3a0902bad 100644 +--- a/arch/x86/lib/insn-eval.c ++++ b/arch/x86/lib/insn-eval.c +@@ -557,7 +557,8 @@ static int get_reg_offset_16(struct insn *insn, struct pt_regs *regs, + } + + /** +- * get_desc() - Obtain pointer to a segment descriptor ++ * get_desc() - Obtain contents of a segment descriptor ++ * @out: Segment descriptor contents on success + * @sel: Segment selector + * + * Given a segment selector, obtain a pointer to the segment descriptor. +@@ -565,18 +566,18 @@ static int get_reg_offset_16(struct insn *insn, struct pt_regs *regs, + * + * Returns: + * +- * Pointer to segment descriptor on success. ++ * True on success, false on failure. + * + * NULL on error. + */ +-static struct desc_struct *get_desc(unsigned short sel) ++static bool get_desc(struct desc_struct *out, unsigned short sel) + { + struct desc_ptr gdt_desc = {0, 0}; + unsigned long desc_base; + + #ifdef CONFIG_MODIFY_LDT_SYSCALL + if ((sel & SEGMENT_TI_MASK) == SEGMENT_LDT) { +- struct desc_struct *desc = NULL; ++ bool success = false; + struct ldt_struct *ldt; + + /* Bits [15:3] contain the index of the desired entry. */ +@@ -584,12 +585,14 @@ static struct desc_struct *get_desc(unsigned short sel) + + mutex_lock(¤t->active_mm->context.lock); + ldt = current->active_mm->context.ldt; +- if (ldt && sel < ldt->nr_entries) +- desc = &ldt->entries[sel]; ++ if (ldt && sel < ldt->nr_entries) { ++ *out = ldt->entries[sel]; ++ success = true; ++ } + + mutex_unlock(¤t->active_mm->context.lock); + +- return desc; ++ return success; + } + #endif + native_store_gdt(&gdt_desc); +@@ -604,9 +607,10 @@ static struct desc_struct *get_desc(unsigned short sel) + desc_base = sel & ~(SEGMENT_RPL_MASK | SEGMENT_TI_MASK); + + if (desc_base > gdt_desc.size) +- return NULL; ++ return false; + +- return (struct desc_struct *)(gdt_desc.address + desc_base); ++ *out = *(struct desc_struct *)(gdt_desc.address + desc_base); ++ return true; + } + + /** +@@ -628,7 +632,7 @@ static struct desc_struct *get_desc(unsigned short sel) + */ + unsigned long insn_get_seg_base(struct pt_regs *regs, int seg_reg_idx) + { +- struct desc_struct *desc; ++ struct desc_struct desc; + short sel; + + sel = get_segment_selector(regs, seg_reg_idx); +@@ -666,11 +670,10 @@ unsigned long insn_get_seg_base(struct pt_regs *regs, int seg_reg_idx) + if (!sel) + return -1L; + +- desc = get_desc(sel); +- if (!desc) ++ if (!get_desc(&desc, sel)) + return -1L; + +- return get_desc_base(desc); ++ return get_desc_base(&desc); + } + + /** +@@ -692,7 +695,7 @@ unsigned long insn_get_seg_base(struct pt_regs *regs, int seg_reg_idx) + */ + static unsigned long get_seg_limit(struct pt_regs *regs, int seg_reg_idx) + { +- struct desc_struct *desc; ++ struct desc_struct desc; + unsigned long limit; + short sel; + +@@ -706,8 +709,7 @@ static unsigned long get_seg_limit(struct pt_regs *regs, int seg_reg_idx) + if (!sel) + return 0; + +- desc = get_desc(sel); +- if (!desc) ++ if (!get_desc(&desc, sel)) + return 0; + + /* +@@ -716,8 +718,8 @@ static unsigned long get_seg_limit(struct pt_regs *regs, int seg_reg_idx) + * not tested when checking the segment limits. In practice, + * this means that the segment ends in (limit << 12) + 0xfff. + */ +- limit = get_desc_limit(desc); +- if (desc->g) ++ limit = get_desc_limit(&desc); ++ if (desc.g) + limit = (limit << 12) + 0xfff; + + return limit; +@@ -741,7 +743,7 @@ static unsigned long get_seg_limit(struct pt_regs *regs, int seg_reg_idx) + */ + int insn_get_code_seg_params(struct pt_regs *regs) + { +- struct desc_struct *desc; ++ struct desc_struct desc; + short sel; + + if (v8086_mode(regs)) +@@ -752,8 +754,7 @@ int insn_get_code_seg_params(struct pt_regs *regs) + if (sel < 0) + return sel; + +- desc = get_desc(sel); +- if (!desc) ++ if (!get_desc(&desc, sel)) + return -EINVAL; + + /* +@@ -761,10 +762,10 @@ int insn_get_code_seg_params(struct pt_regs *regs) + * determines whether a segment contains data or code. If this is a data + * segment, return error. + */ +- if (!(desc->type & BIT(3))) ++ if (!(desc.type & BIT(3))) + return -EINVAL; + +- switch ((desc->l << 1) | desc->d) { ++ switch ((desc.l << 1) | desc.d) { + case 0: /* + * Legacy mode. CS.L=0, CS.D=0. Address and operand size are + * both 16-bit. +-- +cgit diff --git a/tests/cases/CVE-2019-13272.patch b/tests/cases/CVE-2019-13272.patch new file mode 100644 index 0000000..405e8b8 --- /dev/null +++ b/tests/cases/CVE-2019-13272.patch @@ -0,0 +1,55 @@ +From 6994eefb0053799d2e07cd140df6c2ea106c41ee Mon Sep 17 00:00:00 2001 +From: Jann Horn +Date: Thu, 4 Jul 2019 17:32:23 +0200 +Subject: ptrace: Fix ->ptracer_cred handling for PTRACE_TRACEME + +Fix two issues: + +When called for PTRACE_TRACEME, ptrace_link() would obtain an RCU +reference to the parent's objective credentials, then give that pointer +to get_cred(). However, the object lifetime rules for things like +struct cred do not permit unconditionally turning an RCU reference into +a stable reference. + +PTRACE_TRACEME records the parent's credentials as if the parent was +acting as the subject, but that's not the case. If a malicious +unprivileged child uses PTRACE_TRACEME and the parent is privileged, and +at a later point, the parent process becomes attacker-controlled +(because it drops privileges and calls execve()), the attacker ends up +with control over two processes with a privileged ptrace relationship, +which can be abused to ptrace a suid binary and obtain root privileges. + +Fix both of these by always recording the credentials of the process +that is requesting the creation of the ptrace relationship: +current_cred() can't change under us, and current is the proper subject +for access control. + +This change is theoretically userspace-visible, but I am not aware of +any code that it will actually break. + +Fixes: 64b875f7ac8a ("ptrace: Capture the ptracer's creds not PT_PTRACE_CAP") +Signed-off-by: Jann Horn +Acked-by: Oleg Nesterov +Cc: stable@vger.kernel.org +Signed-off-by: Linus Torvalds +--- + kernel/ptrace.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/kernel/ptrace.c b/kernel/ptrace.c +index 8456b6e2205f7..705887f63288d 100644 +--- a/kernel/ptrace.c ++++ b/kernel/ptrace.c +@@ -79,9 +79,7 @@ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent, + */ + static void ptrace_link(struct task_struct *child, struct task_struct *new_parent) + { +- rcu_read_lock(); +- __ptrace_link(child, new_parent, __task_cred(new_parent)); +- rcu_read_unlock(); ++ __ptrace_link(child, new_parent, current_cred()); + } + + /** +-- +cgit diff --git a/tests/cases/CVE-2019-15239.patch b/tests/cases/CVE-2019-15239.patch new file mode 100644 index 0000000..aab4712 --- /dev/null +++ b/tests/cases/CVE-2019-15239.patch @@ -0,0 +1,87 @@ +From 7f582b248d0a86bae5788c548d7bb5bca6f7691a Mon Sep 17 00:00:00 2001 +From: Eric Dumazet +Date: Mon, 14 May 2018 21:14:26 -0700 +Subject: tcp: purge write queue in tcp_connect_init() + +syzkaller found a reliable way to crash the host, hitting a BUG() +in __tcp_retransmit_skb() + +Malicous MSG_FASTOPEN is the root cause. We need to purge write queue +in tcp_connect_init() at the point we init snd_una/write_seq. + +This patch also replaces the BUG() by a less intrusive WARN_ON_ONCE() + +kernel BUG at net/ipv4/tcp_output.c:2837! +invalid opcode: 0000 [#1] SMP KASAN +Dumping ftrace buffer: + (ftrace buffer empty) +Modules linked in: +CPU: 0 PID: 5276 Comm: syz-executor0 Not tainted 4.17.0-rc3+ #51 +Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 +RIP: 0010:__tcp_retransmit_skb+0x2992/0x2eb0 net/ipv4/tcp_output.c:2837 +RSP: 0000:ffff8801dae06ff8 EFLAGS: 00010206 +RAX: ffff8801b9fe61c0 RBX: 00000000ffc18a16 RCX: ffffffff864e1a49 +RDX: 0000000000000100 RSI: ffffffff864e2e12 RDI: 0000000000000005 +RBP: ffff8801dae073a0 R08: ffff8801b9fe61c0 R09: ffffed0039c40dd2 +R10: ffffed0039c40dd2 R11: ffff8801ce206e93 R12: 00000000421eeaad +R13: ffff8801ce206d4e R14: ffff8801ce206cc0 R15: ffff8801cd4f4a80 +FS: 0000000000000000(0000) GS:ffff8801dae00000(0063) knlGS:00000000096bc900 +CS: 0010 DS: 002b ES: 002b CR0: 0000000080050033 +CR2: 0000000020000000 CR3: 00000001c47b6000 CR4: 00000000001406f0 +DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 +DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 +Call Trace: + + tcp_retransmit_skb+0x2e/0x250 net/ipv4/tcp_output.c:2923 + tcp_retransmit_timer+0xc50/0x3060 net/ipv4/tcp_timer.c:488 + tcp_write_timer_handler+0x339/0x960 net/ipv4/tcp_timer.c:573 + tcp_write_timer+0x111/0x1d0 net/ipv4/tcp_timer.c:593 + call_timer_fn+0x230/0x940 kernel/time/timer.c:1326 + expire_timers kernel/time/timer.c:1363 [inline] + __run_timers+0x79e/0xc50 kernel/time/timer.c:1666 + run_timer_softirq+0x4c/0x70 kernel/time/timer.c:1692 + __do_softirq+0x2e0/0xaf5 kernel/softirq.c:285 + invoke_softirq kernel/softirq.c:365 [inline] + irq_exit+0x1d1/0x200 kernel/softirq.c:405 + exiting_irq arch/x86/include/asm/apic.h:525 [inline] + smp_apic_timer_interrupt+0x17e/0x710 arch/x86/kernel/apic/apic.c:1052 + apic_timer_interrupt+0xf/0x20 arch/x86/entry/entry_64.S:863 + +Fixes: cf60af03ca4e ("net-tcp: Fast Open client - sendmsg(MSG_FASTOPEN)") +Signed-off-by: Eric Dumazet +Cc: Yuchung Cheng +Cc: Neal Cardwell +Reported-by: syzbot +Acked-by: Neal Cardwell +Signed-off-by: David S. Miller +--- + net/ipv4/tcp_output.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c +index 383cac0ff0ec0..d07e34f8e3091 100644 +--- a/net/ipv4/tcp_output.c ++++ b/net/ipv4/tcp_output.c +@@ -2833,8 +2833,10 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs) + return -EBUSY; + + if (before(TCP_SKB_CB(skb)->seq, tp->snd_una)) { +- if (before(TCP_SKB_CB(skb)->end_seq, tp->snd_una)) +- BUG(); ++ if (unlikely(before(TCP_SKB_CB(skb)->end_seq, tp->snd_una))) { ++ WARN_ON_ONCE(1); ++ return -EINVAL; ++ } + if (tcp_trim_head(sk, skb, tp->snd_una - TCP_SKB_CB(skb)->seq)) + return -ENOMEM; + } +@@ -3342,6 +3344,7 @@ static void tcp_connect_init(struct sock *sk) + sock_reset_flag(sk, SOCK_DONE); + tp->snd_wnd = 0; + tcp_init_wl(tp, 0); ++ tcp_write_queue_purge(sk); + tp->snd_una = tp->write_seq; + tp->snd_sml = tp->write_seq; + tp->snd_up = tp->write_seq; +-- +cgit diff --git a/tests/cases/CVE-2019-18198.patch b/tests/cases/CVE-2019-18198.patch new file mode 100644 index 0000000..e0b1f2c --- /dev/null +++ b/tests/cases/CVE-2019-18198.patch @@ -0,0 +1,80 @@ +From ca7a03c4175366a92cee0ccc4fec0038c3266e26 Mon Sep 17 00:00:00 2001 +From: "Jason A. Donenfeld" +Date: Tue, 24 Sep 2019 16:01:28 +0200 +Subject: ipv6: do not free rt if FIB_LOOKUP_NOREF is set on suppress rule + +Commit 7d9e5f422150 removed references from certain dsts, but accounting +for this never translated down into the fib6 suppression code. This bug +was triggered by WireGuard users who use wg-quick(8), which uses the +"suppress-prefix" directive to ip-rule(8) for routing all of their +internet traffic without routing loops. The test case added here +causes the reference underflow by causing packets to evaluate a suppress +rule. + +Fixes: 7d9e5f422150 ("ipv6: convert major tx path to use RT6_LOOKUP_F_DST_NOREF") +Signed-off-by: Jason A. Donenfeld +Acked-by: Wei Wang +Signed-off-by: David S. Miller +--- + net/ipv6/fib6_rules.c | 3 ++- + tools/testing/selftests/net/fib_tests.sh | 17 ++++++++++++++++- + 2 files changed, 18 insertions(+), 2 deletions(-) + +diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c +index d22b6c140f231..f9e8fe3ff0c5b 100644 +--- a/net/ipv6/fib6_rules.c ++++ b/net/ipv6/fib6_rules.c +@@ -287,7 +287,8 @@ static bool fib6_rule_suppress(struct fib_rule *rule, struct fib_lookup_arg *arg + return false; + + suppress_route: +- ip6_rt_put(rt); ++ if (!(arg->flags & FIB_LOOKUP_NOREF)) ++ ip6_rt_put(rt); + return true; + } + +diff --git a/tools/testing/selftests/net/fib_tests.sh b/tools/testing/selftests/net/fib_tests.sh +index cba83a12da825..c4ba0ff4a53fa 100755 +--- a/tools/testing/selftests/net/fib_tests.sh ++++ b/tools/testing/selftests/net/fib_tests.sh +@@ -9,7 +9,7 @@ ret=0 + ksft_skip=4 + + # all tests in this script. Can be overridden with -t option +-TESTS="unregister down carrier nexthop ipv6_rt ipv4_rt ipv6_addr_metric ipv4_addr_metric ipv6_route_metrics ipv4_route_metrics ipv4_route_v6_gw rp_filter" ++TESTS="unregister down carrier nexthop suppress ipv6_rt ipv4_rt ipv6_addr_metric ipv4_addr_metric ipv6_route_metrics ipv4_route_metrics ipv4_route_v6_gw rp_filter" + + VERBOSE=0 + PAUSE_ON_FAIL=no +@@ -616,6 +616,20 @@ fib_nexthop_test() + cleanup + } + ++fib_suppress_test() ++{ ++ $IP link add dummy1 type dummy ++ $IP link set dummy1 up ++ $IP -6 route add default dev dummy1 ++ $IP -6 rule add table main suppress_prefixlength 0 ++ ping -f -c 1000 -W 1 1234::1 || true ++ $IP -6 rule del table main suppress_prefixlength 0 ++ $IP link del dummy1 ++ ++ # If we got here without crashing, we're good. ++ return 0 ++} ++ + ################################################################################ + # Tests on route add and replace + +@@ -1593,6 +1607,7 @@ do + fib_carrier_test|carrier) fib_carrier_test;; + fib_rp_filter_test|rp_filter) fib_rp_filter_test;; + fib_nexthop_test|nexthop) fib_nexthop_test;; ++ fib_suppress_test|suppress) fib_suppress_test;; + ipv6_route_test|ipv6_rt) ipv6_route_test;; + ipv4_route_test|ipv4_rt) ipv4_route_test;; + ipv6_addr_metric) ipv6_addr_metric_test;; +-- +cgit diff --git a/tests/cases/CVE-2019-18675.patch b/tests/cases/CVE-2019-18675.patch new file mode 100644 index 0000000..37a7f0f --- /dev/null +++ b/tests/cases/CVE-2019-18675.patch @@ -0,0 +1,122 @@ +From be83bbf806822b1b89e0a0f23cd87cddc409e429 Mon Sep 17 00:00:00 2001 +From: Linus Torvalds +Date: Fri, 11 May 2018 09:52:01 -0700 +Subject: mmap: introduce sane default mmap limits + +The internal VM "mmap()" interfaces are based on the mmap target doing +everything using page indexes rather than byte offsets, because +traditionally (ie 32-bit) we had the situation that the byte offset +didn't fit in a register. So while the mmap virtual address was limited +by the word size of the architecture, the backing store was not. + +So we're basically passing "pgoff" around as a page index, in order to +be able to describe backing store locations that are much bigger than +the word size (think files larger than 4GB etc). + +But while this all makes a ton of sense conceptually, we've been dogged +by various drivers that don't really understand this, and internally +work with byte offsets, and then try to work with the page index by +turning it into a byte offset with "pgoff << PAGE_SHIFT". + +Which obviously can overflow. + +Adding the size of the mapping to it to get the byte offset of the end +of the backing store just exacerbates the problem, and if you then use +this overflow-prone value to check various limits of your device driver +mmap capability, you're just setting yourself up for problems. + +The correct thing for drivers to do is to do their limit math in page +indices, the way the interface is designed. Because the generic mmap +code _does_ test that the index doesn't overflow, since that's what the +mmap code really cares about. + +HOWEVER. + +Finding and fixing various random drivers is a sisyphean task, so let's +just see if we can just make the core mmap() code do the limiting for +us. Realistically, the only "big" backing stores we need to care about +are regular files and block devices, both of which are known to do this +properly, and which have nice well-defined limits for how much data they +can access. + +So let's special-case just those two known cases, and then limit other +random mmap users to a backing store that still fits in "unsigned long". +Realistically, that's not much of a limit at all on 64-bit, and on +32-bit architectures the only worry might be the GPU drivers, which can +have big physical address spaces. + +To make it possible for drivers like that to say that they are 64-bit +clean, this patch does repurpose the "FMODE_UNSIGNED_OFFSET" bit in the +file flags to allow drivers to mark their file descriptors as safe in +the full 64-bit mmap address space. + +[ The timing for doing this is less than optimal, and this should really + go in a merge window. But realistically, this needs wide testing more + than it needs anything else, and being main-line is the only way to do + that. + + So the earlier the better, even if it's outside the proper development + cycle - Linus ] + +Cc: Kees Cook +Cc: Dan Carpenter +Cc: Al Viro +Cc: Willy Tarreau +Cc: Dave Airlie +Signed-off-by: Linus Torvalds +--- + mm/mmap.c | 32 ++++++++++++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + +diff --git a/mm/mmap.c b/mm/mmap.c +index 9d5968d1e8e33..6fc4357600863 100644 +--- a/mm/mmap.c ++++ b/mm/mmap.c +@@ -1324,6 +1324,35 @@ static inline int mlock_future_check(struct mm_struct *mm, + return 0; + } + ++static inline u64 file_mmap_size_max(struct file *file, struct inode *inode) ++{ ++ if (S_ISREG(inode->i_mode)) ++ return inode->i_sb->s_maxbytes; ++ ++ if (S_ISBLK(inode->i_mode)) ++ return MAX_LFS_FILESIZE; ++ ++ /* Special "we do even unsigned file positions" case */ ++ if (file->f_mode & FMODE_UNSIGNED_OFFSET) ++ return 0; ++ ++ /* Yes, random drivers might want more. But I'm tired of buggy drivers */ ++ return ULONG_MAX; ++} ++ ++static inline bool file_mmap_ok(struct file *file, struct inode *inode, ++ unsigned long pgoff, unsigned long len) ++{ ++ u64 maxsize = file_mmap_size_max(file, inode); ++ ++ if (maxsize && len > maxsize) ++ return false; ++ maxsize -= len; ++ if (pgoff > maxsize >> PAGE_SHIFT) ++ return false; ++ return true; ++} ++ + /* + * The caller must hold down_write(¤t->mm->mmap_sem). + */ +@@ -1409,6 +1438,9 @@ unsigned long do_mmap(struct file *file, unsigned long addr, + struct inode *inode = file_inode(file); + unsigned long flags_mask; + ++ if (!file_mmap_ok(file, inode, pgoff, len)) ++ return -EOVERFLOW; ++ + flags_mask = LEGACY_MAP_MASK | file->f_op->mmap_supported_flags; + + switch (flags & MAP_TYPE) { +-- +cgit diff --git a/tests/cases/CVE-2019-18683.patch b/tests/cases/CVE-2019-18683.patch new file mode 100644 index 0000000..0a5a6b9 --- /dev/null +++ b/tests/cases/CVE-2019-18683.patch @@ -0,0 +1,125 @@ +From 6dcd5d7a7a29c1e4b8016a06aed78cd650cd8c27 Mon Sep 17 00:00:00 2001 +From: Alexander Popov +Date: Sun, 3 Nov 2019 23:17:19 +0100 +Subject: media: vivid: Fix wrong locking that causes race conditions on + streaming stop + +There is the same incorrect approach to locking implemented in +vivid_stop_generating_vid_cap(), vivid_stop_generating_vid_out() and +sdr_cap_stop_streaming(). + +These functions are called during streaming stopping with vivid_dev.mutex +locked. And they all do the same mistake while stopping their kthreads, +which need to lock this mutex as well. See the example from +vivid_stop_generating_vid_cap(): + /* shutdown control thread */ + vivid_grab_controls(dev, false); + mutex_unlock(&dev->mutex); + kthread_stop(dev->kthread_vid_cap); + dev->kthread_vid_cap = NULL; + mutex_lock(&dev->mutex); + +But when this mutex is unlocked, another vb2_fop_read() can lock it +instead of vivid_thread_vid_cap() and manipulate the buffer queue. +That causes a use-after-free access later. + +To fix those issues let's: + 1. avoid unlocking the mutex in vivid_stop_generating_vid_cap(), +vivid_stop_generating_vid_out() and sdr_cap_stop_streaming(); + 2. use mutex_trylock() with schedule_timeout_uninterruptible() in +the loops of the vivid kthread handlers. + +Signed-off-by: Alexander Popov +Acked-by: Linus Torvalds +Tested-by: Hans Verkuil +Signed-off-by: Hans Verkuil +Cc: # for v3.18 and up +Signed-off-by: Mauro Carvalho Chehab +--- + drivers/media/platform/vivid/vivid-kthread-cap.c | 8 +++++--- + drivers/media/platform/vivid/vivid-kthread-out.c | 8 +++++--- + drivers/media/platform/vivid/vivid-sdr-cap.c | 8 +++++--- + 3 files changed, 15 insertions(+), 9 deletions(-) + +diff --git a/drivers/media/platform/vivid/vivid-kthread-cap.c b/drivers/media/platform/vivid/vivid-kthread-cap.c +index 9f981e8bae6ea..01a9d671b9477 100644 +--- a/drivers/media/platform/vivid/vivid-kthread-cap.c ++++ b/drivers/media/platform/vivid/vivid-kthread-cap.c +@@ -818,7 +818,11 @@ static int vivid_thread_vid_cap(void *data) + if (kthread_should_stop()) + break; + +- mutex_lock(&dev->mutex); ++ if (!mutex_trylock(&dev->mutex)) { ++ schedule_timeout_uninterruptible(1); ++ continue; ++ } ++ + cur_jiffies = jiffies; + if (dev->cap_seq_resync) { + dev->jiffies_vid_cap = cur_jiffies; +@@ -998,8 +1002,6 @@ void vivid_stop_generating_vid_cap(struct vivid_dev *dev, bool *pstreaming) + + /* shutdown control thread */ + vivid_grab_controls(dev, false); +- mutex_unlock(&dev->mutex); + kthread_stop(dev->kthread_vid_cap); + dev->kthread_vid_cap = NULL; +- mutex_lock(&dev->mutex); + } +diff --git a/drivers/media/platform/vivid/vivid-kthread-out.c b/drivers/media/platform/vivid/vivid-kthread-out.c +index c974235d7de3b..6780687978f93 100644 +--- a/drivers/media/platform/vivid/vivid-kthread-out.c ++++ b/drivers/media/platform/vivid/vivid-kthread-out.c +@@ -166,7 +166,11 @@ static int vivid_thread_vid_out(void *data) + if (kthread_should_stop()) + break; + +- mutex_lock(&dev->mutex); ++ if (!mutex_trylock(&dev->mutex)) { ++ schedule_timeout_uninterruptible(1); ++ continue; ++ } ++ + cur_jiffies = jiffies; + if (dev->out_seq_resync) { + dev->jiffies_vid_out = cur_jiffies; +@@ -344,8 +348,6 @@ void vivid_stop_generating_vid_out(struct vivid_dev *dev, bool *pstreaming) + + /* shutdown control thread */ + vivid_grab_controls(dev, false); +- mutex_unlock(&dev->mutex); + kthread_stop(dev->kthread_vid_out); + dev->kthread_vid_out = NULL; +- mutex_lock(&dev->mutex); + } +diff --git a/drivers/media/platform/vivid/vivid-sdr-cap.c b/drivers/media/platform/vivid/vivid-sdr-cap.c +index 9acc709b0740f..2b7522e16efcd 100644 +--- a/drivers/media/platform/vivid/vivid-sdr-cap.c ++++ b/drivers/media/platform/vivid/vivid-sdr-cap.c +@@ -141,7 +141,11 @@ static int vivid_thread_sdr_cap(void *data) + if (kthread_should_stop()) + break; + +- mutex_lock(&dev->mutex); ++ if (!mutex_trylock(&dev->mutex)) { ++ schedule_timeout_uninterruptible(1); ++ continue; ++ } ++ + cur_jiffies = jiffies; + if (dev->sdr_cap_seq_resync) { + dev->jiffies_sdr_cap = cur_jiffies; +@@ -303,10 +307,8 @@ static void sdr_cap_stop_streaming(struct vb2_queue *vq) + } + + /* shutdown control thread */ +- mutex_unlock(&dev->mutex); + kthread_stop(dev->kthread_sdr_cap); + dev->kthread_sdr_cap = NULL; +- mutex_lock(&dev->mutex); + } + + static void sdr_cap_buf_request_complete(struct vb2_buffer *vb) +-- +cgit diff --git a/tests/cases/CVE-2019-18805.patch b/tests/cases/CVE-2019-18805.patch new file mode 100644 index 0000000..3b68572 --- /dev/null +++ b/tests/cases/CVE-2019-18805.patch @@ -0,0 +1,91 @@ +From 19fad20d15a6494f47f85d869f00b11343ee5c78 Mon Sep 17 00:00:00 2001 +From: ZhangXiaoxu +Date: Tue, 16 Apr 2019 09:47:24 +0800 +Subject: ipv4: set the tcp_min_rtt_wlen range from 0 to one day + +There is a UBSAN report as below: +UBSAN: Undefined behaviour in net/ipv4/tcp_input.c:2877:56 +signed integer overflow: +2147483647 * 1000 cannot be represented in type 'int' +CPU: 3 PID: 0 Comm: swapper/3 Not tainted 5.1.0-rc4-00058-g582549e #1 +Call Trace: + + dump_stack+0x8c/0xba + ubsan_epilogue+0x11/0x60 + handle_overflow+0x12d/0x170 + ? ttwu_do_wakeup+0x21/0x320 + __ubsan_handle_mul_overflow+0x12/0x20 + tcp_ack_update_rtt+0x76c/0x780 + tcp_clean_rtx_queue+0x499/0x14d0 + tcp_ack+0x69e/0x1240 + ? __wake_up_sync_key+0x2c/0x50 + ? update_group_capacity+0x50/0x680 + tcp_rcv_established+0x4e2/0xe10 + tcp_v4_do_rcv+0x22b/0x420 + tcp_v4_rcv+0xfe8/0x1190 + ip_protocol_deliver_rcu+0x36/0x180 + ip_local_deliver+0x15b/0x1a0 + ip_rcv+0xac/0xd0 + __netif_receive_skb_one_core+0x7f/0xb0 + __netif_receive_skb+0x33/0xc0 + netif_receive_skb_internal+0x84/0x1c0 + napi_gro_receive+0x2a0/0x300 + receive_buf+0x3d4/0x2350 + ? detach_buf_split+0x159/0x390 + virtnet_poll+0x198/0x840 + ? reweight_entity+0x243/0x4b0 + net_rx_action+0x25c/0x770 + __do_softirq+0x19b/0x66d + irq_exit+0x1eb/0x230 + do_IRQ+0x7a/0x150 + common_interrupt+0xf/0xf + + +It can be reproduced by: + echo 2147483647 > /proc/sys/net/ipv4/tcp_min_rtt_wlen + +Fixes: f672258391b42 ("tcp: track min RTT using windowed min-filter") +Signed-off-by: ZhangXiaoxu +Signed-off-by: David S. Miller +--- + Documentation/networking/ip-sysctl.txt | 1 + + net/ipv4/sysctl_net_ipv4.c | 5 ++++- + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt +index acdfb5d2bcaa4..e2142fe40cdad 100644 +--- a/Documentation/networking/ip-sysctl.txt ++++ b/Documentation/networking/ip-sysctl.txt +@@ -422,6 +422,7 @@ tcp_min_rtt_wlen - INTEGER + minimum RTT when it is moved to a longer path (e.g., due to traffic + engineering). A longer window makes the filter more resistant to RTT + inflations such as transient congestion. The unit is seconds. ++ Possible values: 0 - 86400 (1 day) + Default: 300 + + tcp_moderate_rcvbuf - BOOLEAN +diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c +index ba0fc4b184656..eeb4041fa5f90 100644 +--- a/net/ipv4/sysctl_net_ipv4.c ++++ b/net/ipv4/sysctl_net_ipv4.c +@@ -49,6 +49,7 @@ static int ip_ping_group_range_min[] = { 0, 0 }; + static int ip_ping_group_range_max[] = { GID_T_MAX, GID_T_MAX }; + static int comp_sack_nr_max = 255; + static u32 u32_max_div_HZ = UINT_MAX / HZ; ++static int one_day_secs = 24 * 3600; + + /* obsolete */ + static int sysctl_tcp_low_latency __read_mostly; +@@ -1151,7 +1152,9 @@ static struct ctl_table ipv4_net_table[] = { + .data = &init_net.ipv4.sysctl_tcp_min_rtt_wlen, + .maxlen = sizeof(int), + .mode = 0644, +- .proc_handler = proc_dointvec ++ .proc_handler = proc_dointvec_minmax, ++ .extra1 = &zero, ++ .extra2 = &one_day_secs + }, + { + .procname = "tcp_autocorking", +-- +cgit diff --git a/tests/cases/CVE-2019-19241.patch b/tests/cases/CVE-2019-19241.patch new file mode 100644 index 0000000..4c2c6a3 --- /dev/null +++ b/tests/cases/CVE-2019-19241.patch @@ -0,0 +1,86 @@ +From d69e07793f891524c6bbf1e75b9ae69db4450953 Mon Sep 17 00:00:00 2001 +From: Jens Axboe +Date: Mon, 25 Nov 2019 17:04:13 -0700 +Subject: net: disallow ancillary data for __sys_{send,recv}msg_file() + +Only io_uring uses (and added) these, and we want to disallow the +use of sendmsg/recvmsg for anything but regular data transfers. +Use the newly added prep helper to split the msghdr copy out from +the core function, to check for msg_control and msg_controllen +settings. If either is set, we return -EINVAL. + +Acked-by: David S. Miller +Signed-off-by: Jens Axboe +--- + net/socket.c | 43 +++++++++++++++++++++++++++++++++++++------ + 1 file changed, 37 insertions(+), 6 deletions(-) + +diff --git a/net/socket.c b/net/socket.c +index 98568ae58bad4..c78c3d37c884f 100644 +--- a/net/socket.c ++++ b/net/socket.c +@@ -2389,12 +2389,27 @@ static int ___sys_sendmsg(struct socket *sock, struct user_msghdr __user *msg, + /* + * BSD sendmsg interface + */ +-long __sys_sendmsg_sock(struct socket *sock, struct user_msghdr __user *msg, ++long __sys_sendmsg_sock(struct socket *sock, struct user_msghdr __user *umsg, + unsigned int flags) + { +- struct msghdr msg_sys; ++ struct iovec iovstack[UIO_FASTIOV], *iov = iovstack; ++ struct sockaddr_storage address; ++ struct msghdr msg = { .msg_name = &address }; ++ ssize_t err; ++ ++ err = sendmsg_copy_msghdr(&msg, umsg, flags, &iov); ++ if (err) ++ return err; ++ /* disallow ancillary data requests from this path */ ++ if (msg.msg_control || msg.msg_controllen) { ++ err = -EINVAL; ++ goto out; ++ } + +- return ___sys_sendmsg(sock, msg, &msg_sys, flags, NULL, 0); ++ err = ____sys_sendmsg(sock, &msg, flags, NULL, 0); ++out: ++ kfree(iov); ++ return err; + } + + long __sys_sendmsg(int fd, struct user_msghdr __user *msg, unsigned int flags, +@@ -2593,12 +2608,28 @@ static int ___sys_recvmsg(struct socket *sock, struct user_msghdr __user *msg, + * BSD recvmsg interface + */ + +-long __sys_recvmsg_sock(struct socket *sock, struct user_msghdr __user *msg, ++long __sys_recvmsg_sock(struct socket *sock, struct user_msghdr __user *umsg, + unsigned int flags) + { +- struct msghdr msg_sys; ++ struct iovec iovstack[UIO_FASTIOV], *iov = iovstack; ++ struct sockaddr_storage address; ++ struct msghdr msg = { .msg_name = &address }; ++ struct sockaddr __user *uaddr; ++ ssize_t err; + +- return ___sys_recvmsg(sock, msg, &msg_sys, flags, 0); ++ err = recvmsg_copy_msghdr(&msg, umsg, flags, &uaddr, &iov); ++ if (err) ++ return err; ++ /* disallow ancillary data requests from this path */ ++ if (msg.msg_control || msg.msg_controllen) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ err = ____sys_recvmsg(sock, &msg, umsg, uaddr, flags, 0); ++out: ++ kfree(iov); ++ return err; + } + + long __sys_recvmsg(int fd, struct user_msghdr __user *msg, unsigned int flags, +-- +cgit diff --git a/tests/cases/CVE-2019-19377.patch b/tests/cases/CVE-2019-19377.patch new file mode 100644 index 0000000..4ca282c --- /dev/null +++ b/tests/cases/CVE-2019-19377.patch @@ -0,0 +1,225 @@ +From b3ff8f1d380e65dddd772542aa9bff6c86bf715a Mon Sep 17 00:00:00 2001 +From: Qu Wenruo +Date: Wed, 12 Feb 2020 14:12:44 +0800 +Subject: btrfs: Don't submit any btree write bio if the fs has errors + +[BUG] +There is a fuzzed image which could cause KASAN report at unmount time. + + BUG: KASAN: use-after-free in btrfs_queue_work+0x2c1/0x390 + Read of size 8 at addr ffff888067cf6848 by task umount/1922 + + CPU: 0 PID: 1922 Comm: umount Tainted: G W 5.0.21 #1 + Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014 + Call Trace: + dump_stack+0x5b/0x8b + print_address_description+0x70/0x280 + kasan_report+0x13a/0x19b + btrfs_queue_work+0x2c1/0x390 + btrfs_wq_submit_bio+0x1cd/0x240 + btree_submit_bio_hook+0x18c/0x2a0 + submit_one_bio+0x1be/0x320 + flush_write_bio.isra.41+0x2c/0x70 + btree_write_cache_pages+0x3bb/0x7f0 + do_writepages+0x5c/0x130 + __writeback_single_inode+0xa3/0x9a0 + writeback_single_inode+0x23d/0x390 + write_inode_now+0x1b5/0x280 + iput+0x2ef/0x600 + close_ctree+0x341/0x750 + generic_shutdown_super+0x126/0x370 + kill_anon_super+0x31/0x50 + btrfs_kill_super+0x36/0x2b0 + deactivate_locked_super+0x80/0xc0 + deactivate_super+0x13c/0x150 + cleanup_mnt+0x9a/0x130 + task_work_run+0x11a/0x1b0 + exit_to_usermode_loop+0x107/0x130 + do_syscall_64+0x1e5/0x280 + entry_SYSCALL_64_after_hwframe+0x44/0xa9 + +[CAUSE] +The fuzzed image has a completely screwd up extent tree: + + leaf 29421568 gen 8 total ptrs 6 free space 3587 owner EXTENT_TREE + refs 2 lock (w:0 r:0 bw:0 br:0 sw:0 sr:0) lock_owner 0 current 5938 + item 0 key (12587008 168 4096) itemoff 3942 itemsize 53 + extent refs 1 gen 9 flags 1 + ref#0: extent data backref root 5 objectid 259 offset 0 count 1 + item 1 key (12591104 168 8192) itemoff 3889 itemsize 53 + extent refs 1 gen 9 flags 1 + ref#0: extent data backref root 5 objectid 271 offset 0 count 1 + item 2 key (12599296 168 4096) itemoff 3836 itemsize 53 + extent refs 1 gen 9 flags 1 + ref#0: extent data backref root 5 objectid 259 offset 4096 count 1 + item 3 key (29360128 169 0) itemoff 3803 itemsize 33 + extent refs 1 gen 9 flags 2 + ref#0: tree block backref root 5 + item 4 key (29368320 169 1) itemoff 3770 itemsize 33 + extent refs 1 gen 9 flags 2 + ref#0: tree block backref root 5 + item 5 key (29372416 169 0) itemoff 3737 itemsize 33 + extent refs 1 gen 9 flags 2 + ref#0: tree block backref root 5 + +Note that leaf 29421568 doesn't have its backref in the extent tree. +Thus extent allocator can re-allocate leaf 29421568 for other trees. + +In short, the bug is caused by: + +- Existing tree block gets allocated to log tree + This got its generation bumped. + +- Log tree balance cleaned dirty bit of offending tree block + It will not be written back to disk, thus no WRITTEN flag. + +- Original owner of the tree block gets COWed + Since the tree block has higher transid, no WRITTEN flag, it's reused, + and not traced by transaction::dirty_pages. + +- Transaction aborted + Tree blocks get cleaned according to transaction::dirty_pages. But the + offending tree block is not recorded at all. + +- Filesystem unmount + All pages are assumed to be are clean, destroying all workqueue, then + call iput(btree_inode). + But offending tree block is still dirty, which triggers writeback, and + causes use-after-free bug. + +The detailed sequence looks like this: + +- Initial status + eb: 29421568, header=WRITTEN bflags_dirty=0, page_dirty=0, gen=8, + not traced by any dirty extent_iot_tree. + +- New tree block is allocated + Since there is no backref for 29421568, it's re-allocated as new tree + block. + Keep in mind that tree block 29421568 is still referred by extent + tree. + +- Tree block 29421568 is filled for log tree + eb: 29421568, header=0 bflags_dirty=1, page_dirty=1, gen=9 << (gen bumped) + traced by btrfs_root::dirty_log_pages + +- Some log tree operations + Since the fs is using node size 4096, the log tree can easily go a + level higher. + +- Log tree needs balance + Tree block 29421568 gets all its content pushed to right, thus now + it is empty, and we don't need it. + btrfs_clean_tree_block() from __push_leaf_right() get called. + + eb: 29421568, header=0 bflags_dirty=0, page_dirty=0, gen=9 + traced by btrfs_root::dirty_log_pages + +- Log tree write back + btree_write_cache_pages() goes through dirty pages ranges, but since + page of tree block 29421568 gets cleaned already, it's not written + back to disk. Thus it doesn't have WRITTEN bit set. + But ranges in dirty_log_pages are cleared. + + eb: 29421568, header=0 bflags_dirty=0, page_dirty=0, gen=9 + not traced by any dirty extent_iot_tree. + +- Extent tree update when committing transaction + Since tree block 29421568 has transid equal to running trans, and has + no WRITTEN bit, should_cow_block() will use it directly without adding + it to btrfs_transaction::dirty_pages. + + eb: 29421568, header=0 bflags_dirty=1, page_dirty=1, gen=9 + not traced by any dirty extent_iot_tree. + + At this stage, we're doomed. We have a dirty eb not tracked by any + extent io tree. + +- Transaction gets aborted due to corrupted extent tree + Btrfs cleans up dirty pages according to transaction::dirty_pages and + btrfs_root::dirty_log_pages. + But since tree block 29421568 is not tracked by neither of them, it's + still dirty. + + eb: 29421568, header=0 bflags_dirty=1, page_dirty=1, gen=9 + not traced by any dirty extent_iot_tree. + +- Filesystem unmount + Since all cleanup is assumed to be done, all workqueus are destroyed. + Then iput(btree_inode) is called, expecting no dirty pages. + But tree 29421568 is still dirty, thus triggering writeback. + Since all workqueues are already freed, we cause use-after-free. + +This shows us that, log tree blocks + bad extent tree can cause wild +dirty pages. + +[FIX] +To fix the problem, don't submit any btree write bio if the filesytem +has any error. This is the last safe net, just in case other cleanup +haven't caught catch it. + +Link: https://github.com/bobfuzzer/CVE/tree/master/CVE-2019-19377 +CC: stable@vger.kernel.org # 5.4+ +Reviewed-by: Josef Bacik +Signed-off-by: Qu Wenruo +Reviewed-by: David Sterba +Signed-off-by: David Sterba +--- + fs/btrfs/extent_io.c | 35 ++++++++++++++++++++++++++++++++++- + 1 file changed, 34 insertions(+), 1 deletion(-) + +diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c +index 14b7007634b75..837262d54e282 100644 +--- a/fs/btrfs/extent_io.c ++++ b/fs/btrfs/extent_io.c +@@ -3956,6 +3956,7 @@ int btree_write_cache_pages(struct address_space *mapping, + .extent_locked = 0, + .sync_io = wbc->sync_mode == WB_SYNC_ALL, + }; ++ struct btrfs_fs_info *fs_info = BTRFS_I(mapping->host)->root->fs_info; + int ret = 0; + int done = 0; + int nr_to_write_done = 0; +@@ -4069,7 +4070,39 @@ retry: + end_write_bio(&epd, ret); + return ret; + } +- ret = flush_write_bio(&epd); ++ /* ++ * If something went wrong, don't allow any metadata write bio to be ++ * submitted. ++ * ++ * This would prevent use-after-free if we had dirty pages not ++ * cleaned up, which can still happen by fuzzed images. ++ * ++ * - Bad extent tree ++ * Allowing existing tree block to be allocated for other trees. ++ * ++ * - Log tree operations ++ * Exiting tree blocks get allocated to log tree, bumps its ++ * generation, then get cleaned in tree re-balance. ++ * Such tree block will not be written back, since it's clean, ++ * thus no WRITTEN flag set. ++ * And after log writes back, this tree block is not traced by ++ * any dirty extent_io_tree. ++ * ++ * - Offending tree block gets re-dirtied from its original owner ++ * Since it has bumped generation, no WRITTEN flag, it can be ++ * reused without COWing. This tree block will not be traced ++ * by btrfs_transaction::dirty_pages. ++ * ++ * Now such dirty tree block will not be cleaned by any dirty ++ * extent io tree. Thus we don't want to submit such wild eb ++ * if the fs already has error. ++ */ ++ if (!test_bit(BTRFS_FS_STATE_ERROR, &fs_info->fs_state)) { ++ ret = flush_write_bio(&epd); ++ } else { ++ ret = -EUCLEAN; ++ end_write_bio(&epd, ret); ++ } + return ret; + } + +-- +cgit diff --git a/tests/cases/CVE-2019-19448.patch b/tests/cases/CVE-2019-19448.patch new file mode 100644 index 0000000..cf07fa0 --- /dev/null +++ b/tests/cases/CVE-2019-19448.patch @@ -0,0 +1,63 @@ +From bf53d4687b8f3f6b752f091eb85f62369a515dfd Mon Sep 17 00:00:00 2001 +From: Josef Bacik +Date: Mon, 27 Jul 2020 10:28:05 -0400 +Subject: btrfs: only search for left_info if there is no right_info in + try_merge_free_space + +In try_to_merge_free_space we attempt to find entries to the left and +right of the entry we are adding to see if they can be merged. We +search for an entry past our current info (saved into right_info), and +then if right_info exists and it has a rb_prev() we save the rb_prev() +into left_info. + +However there's a slight problem in the case that we have a right_info, +but no entry previous to that entry. At that point we will search for +an entry just before the info we're attempting to insert. This will +simply find right_info again, and assign it to left_info, making them +both the same pointer. + +Now if right_info _can_ be merged with the range we're inserting, we'll +add it to the info and free right_info. However further down we'll +access left_info, which was right_info, and thus get a use-after-free. + +Fix this by only searching for the left entry if we don't find a right +entry at all. + +The CVE referenced had a specially crafted file system that could +trigger this use-after-free. However with the tree checker improvements +we no longer trigger the conditions for the UAF. But the original +conditions still apply, hence this fix. + +Reference: CVE-2019-19448 +Fixes: 963030817060 ("Btrfs: use hybrid extents+bitmap rb tree for free space") +CC: stable@vger.kernel.org # 4.4+ +Signed-off-by: Josef Bacik +Signed-off-by: David Sterba +--- + fs/btrfs/free-space-cache.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c +index 6d961e11639e3..ef0fd7afb0b1e 100644 +--- a/fs/btrfs/free-space-cache.c ++++ b/fs/btrfs/free-space-cache.c +@@ -2282,7 +2282,7 @@ out: + static bool try_merge_free_space(struct btrfs_free_space_ctl *ctl, + struct btrfs_free_space *info, bool update_stat) + { +- struct btrfs_free_space *left_info; ++ struct btrfs_free_space *left_info = NULL; + struct btrfs_free_space *right_info; + bool merged = false; + u64 offset = info->offset; +@@ -2298,7 +2298,7 @@ static bool try_merge_free_space(struct btrfs_free_space_ctl *ctl, + if (right_info && rb_prev(&right_info->offset_index)) + left_info = rb_entry(rb_prev(&right_info->offset_index), + struct btrfs_free_space, offset_index); +- else ++ else if (!right_info) + left_info = tree_search_offset(ctl, offset - 1, 0, 0); + + /* See try_merge_free_space() comment. */ +-- +cgit diff --git a/tests/cases/CVE-2019-19449.patch b/tests/cases/CVE-2019-19449.patch new file mode 100644 index 0000000..cdd365a --- /dev/null +++ b/tests/cases/CVE-2019-19449.patch @@ -0,0 +1,67 @@ +From 3a22e9ac71585bcb7667e44641f1bbb25295f0ce Mon Sep 17 00:00:00 2001 +From: Chao Yu +Date: Tue, 29 Sep 2020 09:23:34 +0800 +Subject: f2fs: fix to do sanity check on segment/section count + +As syzbot reported: + +BUG: KASAN: slab-out-of-bounds in init_min_max_mtime fs/f2fs/segment.c:4710 [inline] +BUG: KASAN: slab-out-of-bounds in f2fs_build_segment_manager+0x9302/0xa6d0 fs/f2fs/segment.c:4792 +Read of size 8 at addr ffff8880a1b934a8 by task syz-executor682/6878 + +CPU: 1 PID: 6878 Comm: syz-executor682 Not tainted 5.9.0-rc6-syzkaller #0 +Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 +Call Trace: + __dump_stack lib/dump_stack.c:77 [inline] + dump_stack+0x198/0x1fd lib/dump_stack.c:118 + print_address_description.constprop.0.cold+0xae/0x497 mm/kasan/report.c:383 + __kasan_report mm/kasan/report.c:513 [inline] + kasan_report.cold+0x1f/0x37 mm/kasan/report.c:530 + init_min_max_mtime fs/f2fs/segment.c:4710 [inline] + f2fs_build_segment_manager+0x9302/0xa6d0 fs/f2fs/segment.c:4792 + f2fs_fill_super+0x381a/0x6e80 fs/f2fs/super.c:3633 + mount_bdev+0x32e/0x3f0 fs/super.c:1417 + legacy_get_tree+0x105/0x220 fs/fs_context.c:592 + vfs_get_tree+0x89/0x2f0 fs/super.c:1547 + do_new_mount fs/namespace.c:2875 [inline] + path_mount+0x1387/0x20a0 fs/namespace.c:3192 + do_mount fs/namespace.c:3205 [inline] + __do_sys_mount fs/namespace.c:3413 [inline] + __se_sys_mount fs/namespace.c:3390 [inline] + __x64_sys_mount+0x27f/0x300 fs/namespace.c:3390 + do_syscall_64+0x2d/0x70 arch/x86/entry/common.c:46 + entry_SYSCALL_64_after_hwframe+0x44/0xa9 + +The root cause is: if segs_per_sec is larger than one, and segment count +in last section is less than segs_per_sec, we will suffer out-of-boundary +memory access on sit_i->sentries[] in init_min_max_mtime(). + +Fix this by adding sanity check among segment count, section count and +segs_per_sec value in sanity_check_raw_super(). + +Reported-by: syzbot+481a3ffab50fed41dcc0@syzkaller.appspotmail.com +Signed-off-by: Chao Yu +Signed-off-by: Jaegeuk Kim +--- + fs/f2fs/super.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c +index 4846017177866..adc98c12ed53d 100644 +--- a/fs/f2fs/super.c ++++ b/fs/f2fs/super.c +@@ -2833,6 +2833,12 @@ static int sanity_check_raw_super(struct f2fs_sb_info *sbi, + return -EFSCORRUPTED; + } + ++ if (segment_count_main != total_sections * segs_per_sec) { ++ f2fs_info(sbi, "Invalid segment/section count (%u != %u * %u)", ++ segment_count_main, total_sections, segs_per_sec); ++ return -EFSCORRUPTED; ++ } ++ + if ((segment_count / segs_per_sec) < total_sections) { + f2fs_info(sbi, "Small segment_count (%u < %u * %u)", + segment_count, segs_per_sec, total_sections); +-- +cgit diff --git a/tests/cases/CVE-2019-19816.patch b/tests/cases/CVE-2019-19816.patch new file mode 100644 index 0000000..4b113d3 --- /dev/null +++ b/tests/cases/CVE-2019-19816.patch @@ -0,0 +1,221 @@ +From 6bf9e4bd6a277840d3fe8c5d5d530a1fbd3db592 Mon Sep 17 00:00:00 2001 +From: Qu Wenruo +Date: Wed, 13 Mar 2019 13:55:11 +0800 +Subject: btrfs: inode: Verify inode mode to avoid NULL pointer dereference + +[BUG] +When accessing a file on a crafted image, btrfs can crash in block layer: + + BUG: unable to handle kernel NULL pointer dereference at 0000000000000008 + PGD 136501067 P4D 136501067 PUD 124519067 PMD 0 + CPU: 3 PID: 0 Comm: swapper/3 Not tainted 5.0.0-rc8-default #252 + RIP: 0010:end_bio_extent_readpage+0x144/0x700 + Call Trace: + + blk_update_request+0x8f/0x350 + blk_mq_end_request+0x1a/0x120 + blk_done_softirq+0x99/0xc0 + __do_softirq+0xc7/0x467 + irq_exit+0xd1/0xe0 + call_function_single_interrupt+0xf/0x20 + + RIP: 0010:default_idle+0x1e/0x170 + +[CAUSE] +The crafted image has a tricky corruption, the INODE_ITEM has a +different type against its parent dir: + + item 20 key (268 INODE_ITEM 0) itemoff 2808 itemsize 160 + generation 13 transid 13 size 1048576 nbytes 1048576 + block group 0 mode 121644 links 1 uid 0 gid 0 rdev 0 + sequence 9 flags 0x0(none) + +This mode number 0120000 means it's a symlink. + +But the dir item think it's still a regular file: + + item 8 key (264 DIR_INDEX 5) itemoff 3707 itemsize 32 + location key (268 INODE_ITEM 0) type FILE + transid 13 data_len 0 name_len 2 + name: f4 + item 40 key (264 DIR_ITEM 51821248) itemoff 1573 itemsize 32 + location key (268 INODE_ITEM 0) type FILE + transid 13 data_len 0 name_len 2 + name: f4 + +For symlink, we don't set BTRFS_I(inode)->io_tree.ops and leave it +empty, as symlink is only designed to have inlined extent, all handled +by tree block read. Thus no need to trigger btrfs_submit_bio_hook() for +inline file extent. + +However end_bio_extent_readpage() expects tree->ops populated, as it's +reading regular data extent. This causes NULL pointer dereference. + +[FIX] +This patch fixes the problem in two ways: + +- Verify inode mode against its dir item when looking up inode + So in btrfs_lookup_dentry() if we find inode mode mismatch with dir + item, we error out so that corrupted inode will not be accessed. + +- Verify inode mode when getting extent mapping + Only regular file should have regular or preallocated extent. + If we found regular/preallocated file extent for symlink or + the rest, we error out before submitting the read bio. + +With this fix that crafted image can be rejected gracefully: + + BTRFS critical (device loop0): inode mode mismatch with dir: inode mode=0121644 btrfs type=7 dir type=1 + +Reported-by: Yoon Jungyeon +Link: https://bugzilla.kernel.org/show_bug.cgi?id=202763 +Reviewed-by: Nikolay Borisov +Signed-off-by: Qu Wenruo +Signed-off-by: David Sterba +--- + fs/btrfs/inode.c | 67 ++++++++++++++++++++++++++++++-------------- + fs/btrfs/tests/inode-tests.c | 1 + + 2 files changed, 47 insertions(+), 21 deletions(-) + +diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c +index 1d81a7a78a3ff..baa80d8088064 100644 +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -5437,12 +5437,14 @@ no_delete: + } + + /* +- * this returns the key found in the dir entry in the location pointer. ++ * Return the key found in the dir entry in the location pointer, fill @type ++ * with BTRFS_FT_*, and return 0. ++ * + * If no dir entries were found, returns -ENOENT. + * If found a corrupted location in dir entry, returns -EUCLEAN. + */ + static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry, +- struct btrfs_key *location) ++ struct btrfs_key *location, u8 *type) + { + const char *name = dentry->d_name.name; + int namelen = dentry->d_name.len; +@@ -5471,6 +5473,8 @@ static int btrfs_inode_by_name(struct inode *dir, struct dentry *dentry, + __func__, name, btrfs_ino(BTRFS_I(dir)), + location->objectid, location->type, location->offset); + } ++ if (!ret) ++ *type = btrfs_dir_type(path->nodes[0], di); + out: + btrfs_free_path(path); + return ret; +@@ -5708,6 +5712,24 @@ static struct inode *new_simple_dir(struct super_block *s, + return inode; + } + ++static inline u8 btrfs_inode_type(struct inode *inode) ++{ ++ /* ++ * Compile-time asserts that generic FT_* types still match ++ * BTRFS_FT_* types ++ */ ++ BUILD_BUG_ON(BTRFS_FT_UNKNOWN != FT_UNKNOWN); ++ BUILD_BUG_ON(BTRFS_FT_REG_FILE != FT_REG_FILE); ++ BUILD_BUG_ON(BTRFS_FT_DIR != FT_DIR); ++ BUILD_BUG_ON(BTRFS_FT_CHRDEV != FT_CHRDEV); ++ BUILD_BUG_ON(BTRFS_FT_BLKDEV != FT_BLKDEV); ++ BUILD_BUG_ON(BTRFS_FT_FIFO != FT_FIFO); ++ BUILD_BUG_ON(BTRFS_FT_SOCK != FT_SOCK); ++ BUILD_BUG_ON(BTRFS_FT_SYMLINK != FT_SYMLINK); ++ ++ return fs_umode_to_ftype(inode->i_mode); ++} ++ + struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry) + { + struct btrfs_fs_info *fs_info = btrfs_sb(dir->i_sb); +@@ -5715,18 +5737,31 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry) + struct btrfs_root *root = BTRFS_I(dir)->root; + struct btrfs_root *sub_root = root; + struct btrfs_key location; ++ u8 di_type = 0; + int index; + int ret = 0; + + if (dentry->d_name.len > BTRFS_NAME_LEN) + return ERR_PTR(-ENAMETOOLONG); + +- ret = btrfs_inode_by_name(dir, dentry, &location); ++ ret = btrfs_inode_by_name(dir, dentry, &location, &di_type); + if (ret < 0) + return ERR_PTR(ret); + + if (location.type == BTRFS_INODE_ITEM_KEY) { + inode = btrfs_iget(dir->i_sb, &location, root, NULL); ++ if (IS_ERR(inode)) ++ return inode; ++ ++ /* Do extra check against inode mode with di_type */ ++ if (btrfs_inode_type(inode) != di_type) { ++ btrfs_crit(fs_info, ++"inode mode mismatch with dir: inode mode=0%o btrfs type=%u dir type=%u", ++ inode->i_mode, btrfs_inode_type(inode), ++ di_type); ++ iput(inode); ++ return ERR_PTR(-EUCLEAN); ++ } + return inode; + } + +@@ -6327,24 +6362,6 @@ fail: + return ERR_PTR(ret); + } + +-static inline u8 btrfs_inode_type(struct inode *inode) +-{ +- /* +- * Compile-time asserts that generic FT_* types still match +- * BTRFS_FT_* types +- */ +- BUILD_BUG_ON(BTRFS_FT_UNKNOWN != FT_UNKNOWN); +- BUILD_BUG_ON(BTRFS_FT_REG_FILE != FT_REG_FILE); +- BUILD_BUG_ON(BTRFS_FT_DIR != FT_DIR); +- BUILD_BUG_ON(BTRFS_FT_CHRDEV != FT_CHRDEV); +- BUILD_BUG_ON(BTRFS_FT_BLKDEV != FT_BLKDEV); +- BUILD_BUG_ON(BTRFS_FT_FIFO != FT_FIFO); +- BUILD_BUG_ON(BTRFS_FT_SOCK != FT_SOCK); +- BUILD_BUG_ON(BTRFS_FT_SYMLINK != FT_SYMLINK); +- +- return fs_umode_to_ftype(inode->i_mode); +-} +- + /* + * utility function to add 'inode' into 'parent_inode' with + * a give name and a given sequence number. +@@ -6862,6 +6879,14 @@ struct extent_map *btrfs_get_extent(struct btrfs_inode *inode, + extent_start = found_key.offset; + if (extent_type == BTRFS_FILE_EXTENT_REG || + extent_type == BTRFS_FILE_EXTENT_PREALLOC) { ++ /* Only regular file could have regular/prealloc extent */ ++ if (!S_ISREG(inode->vfs_inode.i_mode)) { ++ ret = -EUCLEAN; ++ btrfs_crit(fs_info, ++ "regular/prealloc extent found for non-regular inode %llu", ++ btrfs_ino(inode)); ++ goto out; ++ } + extent_end = extent_start + + btrfs_file_extent_num_bytes(leaf, item); + +diff --git a/fs/btrfs/tests/inode-tests.c b/fs/btrfs/tests/inode-tests.c +index 3d2c7abda5de5..bc6dbd1b42fd0 100644 +--- a/fs/btrfs/tests/inode-tests.c ++++ b/fs/btrfs/tests/inode-tests.c +@@ -234,6 +234,7 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize) + return ret; + } + ++ inode->i_mode = S_IFREG; + BTRFS_I(inode)->location.type = BTRFS_INODE_ITEM_KEY; + BTRFS_I(inode)->location.objectid = BTRFS_FIRST_FREE_OBJECTID; + BTRFS_I(inode)->location.offset = 0; +-- +cgit diff --git a/tests/cases/CVE-2019-25044.patch b/tests/cases/CVE-2019-25044.patch new file mode 100644 index 0000000..4f0f758 --- /dev/null +++ b/tests/cases/CVE-2019-25044.patch @@ -0,0 +1,204 @@ +From c3e2219216c92919a6bd1711f340f5faa98695e6 Mon Sep 17 00:00:00 2001 +From: Ming Lei +Date: Tue, 4 Jun 2019 21:08:02 +0800 +Subject: block: free sched's request pool in blk_cleanup_queue + +In theory, IO scheduler belongs to request queue, and the request pool +of sched tags belongs to the request queue too. + +However, the current tags allocation interfaces are re-used for both +driver tags and sched tags, and driver tags is definitely host wide, +and doesn't belong to any request queue, same with its request pool. +So we need tagset instance for freeing request of sched tags. + +Meantime, blk_mq_free_tag_set() often follows blk_cleanup_queue() in case +of non-BLK_MQ_F_TAG_SHARED, this way requires that request pool of sched +tags to be freed before calling blk_mq_free_tag_set(). + +Commit 47cdee29ef9d94e ("block: move blk_exit_queue into __blk_release_queue") +moves blk_exit_queue into __blk_release_queue for simplying the fast +path in generic_make_request(), then causes oops during freeing requests +of sched tags in __blk_release_queue(). + +Fix the above issue by move freeing request pool of sched tags into +blk_cleanup_queue(), this way is safe becasue queue has been frozen and no any +in-queue requests at that time. Freeing sched tags has to be kept in queue's +release handler becasue there might be un-completed dispatch activity +which might refer to sched tags. + +Cc: Bart Van Assche +Cc: Christoph Hellwig +Fixes: 47cdee29ef9d94e485eb08f962c74943023a5271 ("block: move blk_exit_queue into __blk_release_queue") +Tested-by: Yi Zhang +Reported-by: kernel test robot +Signed-off-by: Ming Lei +Signed-off-by: Jens Axboe +--- + block/blk-core.c | 13 +++++++++++++ + block/blk-mq-sched.c | 30 +++++++++++++++++++++++++++--- + block/blk-mq-sched.h | 1 + + block/blk-sysfs.c | 2 +- + block/blk.h | 10 +++++++++- + block/elevator.c | 2 +- + 6 files changed, 52 insertions(+), 6 deletions(-) + +diff --git a/block/blk-core.c b/block/blk-core.c +index ee1b35fe85726..8340f69670d89 100644 +--- a/block/blk-core.c ++++ b/block/blk-core.c +@@ -320,6 +320,19 @@ void blk_cleanup_queue(struct request_queue *q) + if (queue_is_mq(q)) + blk_mq_exit_queue(q); + ++ /* ++ * In theory, request pool of sched_tags belongs to request queue. ++ * However, the current implementation requires tag_set for freeing ++ * requests, so free the pool now. ++ * ++ * Queue has become frozen, there can't be any in-queue requests, so ++ * it is safe to free requests now. ++ */ ++ mutex_lock(&q->sysfs_lock); ++ if (q->elevator) ++ blk_mq_sched_free_requests(q); ++ mutex_unlock(&q->sysfs_lock); ++ + percpu_ref_exit(&q->q_usage_counter); + + /* @q is and will stay empty, shutdown and put */ +diff --git a/block/blk-mq-sched.c b/block/blk-mq-sched.c +index 74c6bb871f7e6..500cb04901cc1 100644 +--- a/block/blk-mq-sched.c ++++ b/block/blk-mq-sched.c +@@ -475,14 +475,18 @@ static int blk_mq_sched_alloc_tags(struct request_queue *q, + return ret; + } + ++/* called in queue's release handler, tagset has gone away */ + static void blk_mq_sched_tags_teardown(struct request_queue *q) + { +- struct blk_mq_tag_set *set = q->tag_set; + struct blk_mq_hw_ctx *hctx; + int i; + +- queue_for_each_hw_ctx(q, hctx, i) +- blk_mq_sched_free_tags(set, hctx, i); ++ queue_for_each_hw_ctx(q, hctx, i) { ++ if (hctx->sched_tags) { ++ blk_mq_free_rq_map(hctx->sched_tags); ++ hctx->sched_tags = NULL; ++ } ++ } + } + + int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e) +@@ -523,6 +527,7 @@ int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e) + ret = e->ops.init_hctx(hctx, i); + if (ret) { + eq = q->elevator; ++ blk_mq_sched_free_requests(q); + blk_mq_exit_sched(q, eq); + kobject_put(&eq->kobj); + return ret; +@@ -534,11 +539,30 @@ int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e) + return 0; + + err: ++ blk_mq_sched_free_requests(q); + blk_mq_sched_tags_teardown(q); + q->elevator = NULL; + return ret; + } + ++/* ++ * called in either blk_queue_cleanup or elevator_switch, tagset ++ * is required for freeing requests ++ */ ++void blk_mq_sched_free_requests(struct request_queue *q) ++{ ++ struct blk_mq_hw_ctx *hctx; ++ int i; ++ ++ lockdep_assert_held(&q->sysfs_lock); ++ WARN_ON(!q->elevator); ++ ++ queue_for_each_hw_ctx(q, hctx, i) { ++ if (hctx->sched_tags) ++ blk_mq_free_rqs(q->tag_set, hctx->sched_tags, i); ++ } ++} ++ + void blk_mq_exit_sched(struct request_queue *q, struct elevator_queue *e) + { + struct blk_mq_hw_ctx *hctx; +diff --git a/block/blk-mq-sched.h b/block/blk-mq-sched.h +index c7bdb52367ac2..3cf92cbbd8ac1 100644 +--- a/block/blk-mq-sched.h ++++ b/block/blk-mq-sched.h +@@ -28,6 +28,7 @@ void blk_mq_sched_dispatch_requests(struct blk_mq_hw_ctx *hctx); + + int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e); + void blk_mq_exit_sched(struct request_queue *q, struct elevator_queue *e); ++void blk_mq_sched_free_requests(struct request_queue *q); + + static inline bool + blk_mq_sched_bio_merge(struct request_queue *q, struct bio *bio) +diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c +index 75b5281cc5772..977c659dcd184 100644 +--- a/block/blk-sysfs.c ++++ b/block/blk-sysfs.c +@@ -850,7 +850,7 @@ static void blk_exit_queue(struct request_queue *q) + */ + if (q->elevator) { + ioc_clear_queue(q); +- elevator_exit(q, q->elevator); ++ __elevator_exit(q, q->elevator); + q->elevator = NULL; + } + +diff --git a/block/blk.h b/block/blk.h +index 91b3581b7c7a4..7814aa207153c 100644 +--- a/block/blk.h ++++ b/block/blk.h +@@ -6,6 +6,7 @@ + #include + #include + #include "blk-mq.h" ++#include "blk-mq-sched.h" + + /* Max future timer expiry for timeouts */ + #define BLK_MAX_TIMEOUT (5 * HZ) +@@ -176,10 +177,17 @@ void blk_insert_flush(struct request *rq); + int elevator_init_mq(struct request_queue *q); + int elevator_switch_mq(struct request_queue *q, + struct elevator_type *new_e); +-void elevator_exit(struct request_queue *, struct elevator_queue *); ++void __elevator_exit(struct request_queue *, struct elevator_queue *); + int elv_register_queue(struct request_queue *q); + void elv_unregister_queue(struct request_queue *q); + ++static inline void elevator_exit(struct request_queue *q, ++ struct elevator_queue *e) ++{ ++ blk_mq_sched_free_requests(q); ++ __elevator_exit(q, e); ++} ++ + struct hd_struct *__disk_get_part(struct gendisk *disk, int partno); + + #ifdef CONFIG_FAIL_IO_TIMEOUT +diff --git a/block/elevator.c b/block/elevator.c +index ec55d5fc0b3e7..2f17d66d0e617 100644 +--- a/block/elevator.c ++++ b/block/elevator.c +@@ -178,7 +178,7 @@ static void elevator_release(struct kobject *kobj) + kfree(e); + } + +-void elevator_exit(struct request_queue *q, struct elevator_queue *e) ++void __elevator_exit(struct request_queue *q, struct elevator_queue *e) + { + mutex_lock(&e->sysfs_lock); + if (e->type->ops.exit_sched) +-- +cgit diff --git a/tests/cases/CVE-2019-25045.patch b/tests/cases/CVE-2019-25045.patch new file mode 100644 index 0000000..9f70292 --- /dev/null +++ b/tests/cases/CVE-2019-25045.patch @@ -0,0 +1,133 @@ +From dbb2483b2a46fbaf833cfb5deb5ed9cace9c7399 Mon Sep 17 00:00:00 2001 +From: Cong Wang +Date: Fri, 22 Mar 2019 16:26:19 -0700 +Subject: xfrm: clean up xfrm protocol checks + +In commit 6a53b7593233 ("xfrm: check id proto in validate_tmpl()") +I introduced a check for xfrm protocol, but according to Herbert +IPSEC_PROTO_ANY should only be used as a wildcard for lookup, so +it should be removed from validate_tmpl(). + +And, IPSEC_PROTO_ANY is expected to only match 3 IPSec-specific +protocols, this is why xfrm_state_flush() could still miss +IPPROTO_ROUTING, which leads that those entries are left in +net->xfrm.state_all before exit net. Fix this by replacing +IPSEC_PROTO_ANY with zero. + +This patch also extracts the check from validate_tmpl() to +xfrm_id_proto_valid() and uses it in parse_ipsecrequest(). +With this, no other protocols should be added into xfrm. + +Fixes: 6a53b7593233 ("xfrm: check id proto in validate_tmpl()") +Reported-by: syzbot+0bf0519d6e0de15914fe@syzkaller.appspotmail.com +Cc: Steffen Klassert +Cc: Herbert Xu +Signed-off-by: Cong Wang +Acked-by: Herbert Xu +Signed-off-by: Steffen Klassert +--- + include/net/xfrm.h | 17 +++++++++++++++++ + net/ipv6/xfrm6_tunnel.c | 2 +- + net/key/af_key.c | 4 +++- + net/xfrm/xfrm_state.c | 2 +- + net/xfrm/xfrm_user.c | 14 +------------- + 5 files changed, 23 insertions(+), 16 deletions(-) + +diff --git a/include/net/xfrm.h b/include/net/xfrm.h +index 85386becbaea2..902437dfbce77 100644 +--- a/include/net/xfrm.h ++++ b/include/net/xfrm.h +@@ -1404,6 +1404,23 @@ static inline int xfrm_state_kern(const struct xfrm_state *x) + return atomic_read(&x->tunnel_users); + } + ++static inline bool xfrm_id_proto_valid(u8 proto) ++{ ++ switch (proto) { ++ case IPPROTO_AH: ++ case IPPROTO_ESP: ++ case IPPROTO_COMP: ++#if IS_ENABLED(CONFIG_IPV6) ++ case IPPROTO_ROUTING: ++ case IPPROTO_DSTOPTS: ++#endif ++ return true; ++ default: ++ return false; ++ } ++} ++ ++/* IPSEC_PROTO_ANY only matches 3 IPsec protocols, 0 could match all. */ + static inline int xfrm_id_proto_match(u8 proto, u8 userproto) + { + return (!userproto || proto == userproto || +diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c +index 12cb3aa990af4..d9e5f6808811a 100644 +--- a/net/ipv6/xfrm6_tunnel.c ++++ b/net/ipv6/xfrm6_tunnel.c +@@ -345,7 +345,7 @@ static void __net_exit xfrm6_tunnel_net_exit(struct net *net) + unsigned int i; + + xfrm_flush_gc(); +- xfrm_state_flush(net, IPSEC_PROTO_ANY, false, true); ++ xfrm_state_flush(net, 0, false, true); + + for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++) + WARN_ON_ONCE(!hlist_empty(&xfrm6_tn->spi_byaddr[i])); +diff --git a/net/key/af_key.c b/net/key/af_key.c +index 5651c29cb5bd0..4af1e1d60b9f2 100644 +--- a/net/key/af_key.c ++++ b/net/key/af_key.c +@@ -1951,8 +1951,10 @@ parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq) + + if (rq->sadb_x_ipsecrequest_mode == 0) + return -EINVAL; ++ if (!xfrm_id_proto_valid(rq->sadb_x_ipsecrequest_proto)) ++ return -EINVAL; + +- t->id.proto = rq->sadb_x_ipsecrequest_proto; /* XXX check proto */ ++ t->id.proto = rq->sadb_x_ipsecrequest_proto; + if ((mode = pfkey_mode_to_xfrm(rq->sadb_x_ipsecrequest_mode)) < 0) + return -EINVAL; + t->mode = mode; +diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c +index 1bb971f46fc6f..178baaa037e5b 100644 +--- a/net/xfrm/xfrm_state.c ++++ b/net/xfrm/xfrm_state.c +@@ -2384,7 +2384,7 @@ void xfrm_state_fini(struct net *net) + + flush_work(&net->xfrm.state_hash_work); + flush_work(&xfrm_state_gc_work); +- xfrm_state_flush(net, IPSEC_PROTO_ANY, false, true); ++ xfrm_state_flush(net, 0, false, true); + + WARN_ON(!list_empty(&net->xfrm.state_all)); + +diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c +index 8d4d52fd457b2..6916931b1de1c 100644 +--- a/net/xfrm/xfrm_user.c ++++ b/net/xfrm/xfrm_user.c +@@ -1513,20 +1513,8 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family) + return -EINVAL; + } + +- switch (ut[i].id.proto) { +- case IPPROTO_AH: +- case IPPROTO_ESP: +- case IPPROTO_COMP: +-#if IS_ENABLED(CONFIG_IPV6) +- case IPPROTO_ROUTING: +- case IPPROTO_DSTOPTS: +-#endif +- case IPSEC_PROTO_ANY: +- break; +- default: ++ if (!xfrm_id_proto_valid(ut[i].id.proto)) + return -EINVAL; +- } +- + } + + return 0; +-- +cgit diff --git a/tests/cases/CVE-2019-6974.patch b/tests/cases/CVE-2019-6974.patch new file mode 100644 index 0000000..4b71770 --- /dev/null +++ b/tests/cases/CVE-2019-6974.patch @@ -0,0 +1,55 @@ +From cfa39381173d5f969daf43582c95ad679189cbc9 Mon Sep 17 00:00:00 2001 +From: Jann Horn +Date: Sat, 26 Jan 2019 01:54:33 +0100 +Subject: kvm: fix kvm_ioctl_create_device() reference counting (CVE-2019-6974) + +kvm_ioctl_create_device() does the following: + +1. creates a device that holds a reference to the VM object (with a borrowed + reference, the VM's refcount has not been bumped yet) +2. initializes the device +3. transfers the reference to the device to the caller's file descriptor table +4. calls kvm_get_kvm() to turn the borrowed reference to the VM into a real + reference + +The ownership transfer in step 3 must not happen before the reference to the VM +becomes a proper, non-borrowed reference, which only happens in step 4. +After step 3, an attacker can close the file descriptor and drop the borrowed +reference, which can cause the refcount of the kvm object to drop to zero. + +This means that we need to grab a reference for the device before +anon_inode_getfd(), otherwise the VM can disappear from under us. + +Fixes: 852b6d57dc7f ("kvm: add device control API") +Cc: stable@kernel.org +Signed-off-by: Jann Horn +Signed-off-by: Paolo Bonzini +--- + virt/kvm/kvm_main.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c +index 5ecea812cb6a2..585845203db89 100644 +--- a/virt/kvm/kvm_main.c ++++ b/virt/kvm/kvm_main.c +@@ -3000,8 +3000,10 @@ static int kvm_ioctl_create_device(struct kvm *kvm, + if (ops->init) + ops->init(dev); + ++ kvm_get_kvm(kvm); + ret = anon_inode_getfd(ops->name, &kvm_device_fops, dev, O_RDWR | O_CLOEXEC); + if (ret < 0) { ++ kvm_put_kvm(kvm); + mutex_lock(&kvm->lock); + list_del(&dev->vm_node); + mutex_unlock(&kvm->lock); +@@ -3009,7 +3011,6 @@ static int kvm_ioctl_create_device(struct kvm *kvm, + return ret; + } + +- kvm_get_kvm(kvm); + cd->fd = ret; + return 0; + } +-- +cgit diff --git a/tests/cases/CVE-2019-8912.patch b/tests/cases/CVE-2019-8912.patch new file mode 100644 index 0000000..4a046d7 --- /dev/null +++ b/tests/cases/CVE-2019-8912.patch @@ -0,0 +1,120 @@ +From 9060cb719e61b685ec0102574e10337fa5f445ea Mon Sep 17 00:00:00 2001 +From: Mao Wenan +Date: Mon, 18 Feb 2019 10:44:44 +0800 +Subject: net: crypto set sk to NULL when af_alg_release. + +KASAN has found use-after-free in sockfs_setattr. +The existed commit 6d8c50dcb029 ("socket: close race condition between sock_close() +and sockfs_setattr()") is to fix this simillar issue, but it seems to ignore +that crypto module forgets to set the sk to NULL after af_alg_release. + +KASAN report details as below: +BUG: KASAN: use-after-free in sockfs_setattr+0x120/0x150 +Write of size 4 at addr ffff88837b956128 by task syz-executor0/4186 + +CPU: 2 PID: 4186 Comm: syz-executor0 Not tainted xxx + #1 +Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS +1.10.2-1ubuntu1 04/01/2014 +Call Trace: + dump_stack+0xca/0x13e + print_address_description+0x79/0x330 + ? vprintk_func+0x5e/0xf0 + kasan_report+0x18a/0x2e0 + ? sockfs_setattr+0x120/0x150 + sockfs_setattr+0x120/0x150 + ? sock_register+0x2d0/0x2d0 + notify_change+0x90c/0xd40 + ? chown_common+0x2ef/0x510 + chown_common+0x2ef/0x510 + ? chmod_common+0x3b0/0x3b0 + ? __lock_is_held+0xbc/0x160 + ? __sb_start_write+0x13d/0x2b0 + ? __mnt_want_write+0x19a/0x250 + do_fchownat+0x15c/0x190 + ? __ia32_sys_chmod+0x80/0x80 + ? trace_hardirqs_on_thunk+0x1a/0x1c + __x64_sys_fchownat+0xbf/0x160 + ? lockdep_hardirqs_on+0x39a/0x5e0 + do_syscall_64+0xc8/0x580 + entry_SYSCALL_64_after_hwframe+0x49/0xbe +RIP: 0033:0x462589 +Code: f7 d8 64 89 02 b8 ff ff ff ff c3 66 0f 1f 44 00 00 48 89 f8 48 89 +f7 48 89 d6 48 89 +ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 +48 c7 c1 bc ff ff +ff f7 d8 64 89 01 48 +RSP: 002b:00007fb4b2c83c58 EFLAGS: 00000246 ORIG_RAX: 0000000000000104 +RAX: ffffffffffffffda RBX: 000000000072bfa0 RCX: 0000000000462589 +RDX: 0000000000000000 RSI: 00000000200000c0 RDI: 0000000000000007 +RBP: 0000000000000005 R08: 0000000000001000 R09: 0000000000000000 +R10: 0000000000000000 R11: 0000000000000246 R12: 00007fb4b2c846bc +R13: 00000000004bc733 R14: 00000000006f5138 R15: 00000000ffffffff + +Allocated by task 4185: + kasan_kmalloc+0xa0/0xd0 + __kmalloc+0x14a/0x350 + sk_prot_alloc+0xf6/0x290 + sk_alloc+0x3d/0xc00 + af_alg_accept+0x9e/0x670 + hash_accept+0x4a3/0x650 + __sys_accept4+0x306/0x5c0 + __x64_sys_accept4+0x98/0x100 + do_syscall_64+0xc8/0x580 + entry_SYSCALL_64_after_hwframe+0x49/0xbe + +Freed by task 4184: + __kasan_slab_free+0x12e/0x180 + kfree+0xeb/0x2f0 + __sk_destruct+0x4e6/0x6a0 + sk_destruct+0x48/0x70 + __sk_free+0xa9/0x270 + sk_free+0x2a/0x30 + af_alg_release+0x5c/0x70 + __sock_release+0xd3/0x280 + sock_close+0x1a/0x20 + __fput+0x27f/0x7f0 + task_work_run+0x136/0x1b0 + exit_to_usermode_loop+0x1a7/0x1d0 + do_syscall_64+0x461/0x580 + entry_SYSCALL_64_after_hwframe+0x49/0xbe + +Syzkaller reproducer: +r0 = perf_event_open(&(0x7f0000000000)={0x0, 0x70, 0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, @perf_config_ext}, 0x0, 0x0, +0xffffffffffffffff, 0x0) +r1 = socket$alg(0x26, 0x5, 0x0) +getrusage(0x0, 0x0) +bind(r1, &(0x7f00000001c0)=@alg={0x26, 'hash\x00', 0x0, 0x0, +'sha256-ssse3\x00'}, 0x80) +r2 = accept(r1, 0x0, 0x0) +r3 = accept4$unix(r2, 0x0, 0x0, 0x0) +r4 = dup3(r3, r0, 0x0) +fchownat(r4, &(0x7f00000000c0)='\x00', 0x0, 0x0, 0x1000) + +Fixes: 6d8c50dcb029 ("socket: close race condition between sock_close() and sockfs_setattr()") +Signed-off-by: Mao Wenan +Signed-off-by: David S. Miller +--- + crypto/af_alg.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/crypto/af_alg.c b/crypto/af_alg.c +index 17eb09d222ff4..ec78a04eb136e 100644 +--- a/crypto/af_alg.c ++++ b/crypto/af_alg.c +@@ -122,8 +122,10 @@ static void alg_do_release(const struct af_alg_type *type, void *private) + + int af_alg_release(struct socket *sock) + { +- if (sock->sk) ++ if (sock->sk) { + sock_put(sock->sk); ++ sock->sk = NULL; ++ } + return 0; + } + EXPORT_SYMBOL_GPL(af_alg_release); +-- +cgit diff --git a/tests/cases/CVE-2019-8956.patch b/tests/cases/CVE-2019-8956.patch new file mode 100644 index 0000000..5bdc002 --- /dev/null +++ b/tests/cases/CVE-2019-8956.patch @@ -0,0 +1,44 @@ +From ba59fb0273076637f0add4311faa990a5eec27c0 Mon Sep 17 00:00:00 2001 +From: Greg Kroah-Hartman +Date: Fri, 1 Feb 2019 15:15:22 +0100 +Subject: sctp: walk the list of asoc safely + +In sctp_sendmesg(), when walking the list of endpoint associations, the +association can be dropped from the list, making the list corrupt. +Properly handle this by using list_for_each_entry_safe() + +Fixes: 4910280503f3 ("sctp: add support for snd flag SCTP_SENDALL process in sendmsg") +Reported-by: Secunia Research +Tested-by: Secunia Research +Signed-off-by: Greg Kroah-Hartman +Acked-by: Marcelo Ricardo Leitner +Acked-by: Neil Horman +Signed-off-by: David S. Miller +--- + net/sctp/socket.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/net/sctp/socket.c b/net/sctp/socket.c +index f93c3cf9e5674..65d6d04546aee 100644 +--- a/net/sctp/socket.c ++++ b/net/sctp/socket.c +@@ -2027,7 +2027,7 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len) + struct sctp_endpoint *ep = sctp_sk(sk)->ep; + struct sctp_transport *transport = NULL; + struct sctp_sndrcvinfo _sinfo, *sinfo; +- struct sctp_association *asoc; ++ struct sctp_association *asoc, *tmp; + struct sctp_cmsgs cmsgs; + union sctp_addr *daddr; + bool new = false; +@@ -2053,7 +2053,7 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len) + + /* SCTP_SENDALL process */ + if ((sflags & SCTP_SENDALL) && sctp_style(sk, UDP)) { +- list_for_each_entry(asoc, &ep->asocs, asocs) { ++ list_for_each_entry_safe(asoc, tmp, &ep->asocs, asocs) { + err = sctp_sendmsg_check_sflags(asoc, sflags, msg, + msg_len); + if (err == 0) +-- +cgit diff --git a/tests/cases/CVE-2019-9162.patch b/tests/cases/CVE-2019-9162.patch new file mode 100644 index 0000000..cab8763 --- /dev/null +++ b/tests/cases/CVE-2019-9162.patch @@ -0,0 +1,48 @@ +From c4c07b4d6fa1f11880eab8e076d3d060ef3f55fc Mon Sep 17 00:00:00 2001 +From: Jann Horn +Date: Wed, 6 Feb 2019 22:56:15 +0100 +Subject: netfilter: nf_nat_snmp_basic: add missing length checks in ASN.1 cbs + +The generic ASN.1 decoder infrastructure doesn't guarantee that callbacks +will get as much data as they expect; callbacks have to check the `datalen` +parameter before looking at `data`. Make sure that snmp_version() and +snmp_helper() don't read/write beyond the end of the packet data. + +(Also move the assignment to `pdata` down below the check to make it clear +that it isn't necessarily a pointer we can use before the `datalen` check.) + +Fixes: cc2d58634e0f ("netfilter: nf_nat_snmp_basic: use asn1 decoder library") +Signed-off-by: Jann Horn +Signed-off-by: Pablo Neira Ayuso +--- + net/ipv4/netfilter/nf_nat_snmp_basic_main.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic_main.c b/net/ipv4/netfilter/nf_nat_snmp_basic_main.c +index a0aa13bcabda0..0a8a60c1bf9af 100644 +--- a/net/ipv4/netfilter/nf_nat_snmp_basic_main.c ++++ b/net/ipv4/netfilter/nf_nat_snmp_basic_main.c +@@ -105,6 +105,8 @@ static void fast_csum(struct snmp_ctx *ctx, unsigned char offset) + int snmp_version(void *context, size_t hdrlen, unsigned char tag, + const void *data, size_t datalen) + { ++ if (datalen != 1) ++ return -EINVAL; + if (*(unsigned char *)data > 1) + return -ENOTSUPP; + return 1; +@@ -114,8 +116,11 @@ int snmp_helper(void *context, size_t hdrlen, unsigned char tag, + const void *data, size_t datalen) + { + struct snmp_ctx *ctx = (struct snmp_ctx *)context; +- __be32 *pdata = (__be32 *)data; ++ __be32 *pdata; + ++ if (datalen != 4) ++ return -EINVAL; ++ pdata = (__be32 *)data; + if (*pdata == ctx->from) { + pr_debug("%s: %pI4 to %pI4\n", __func__, + (void *)&ctx->from, (void *)&ctx->to); +-- +cgit diff --git a/tests/cases/CVE-2019-9500.patch b/tests/cases/CVE-2019-9500.patch new file mode 100644 index 0000000..ed225db --- /dev/null +++ b/tests/cases/CVE-2019-9500.patch @@ -0,0 +1,32 @@ +From 1b5e2423164b3670e8bc9174e4762d297990deff Mon Sep 17 00:00:00 2001 +From: Arend van Spriel +Date: Thu, 14 Feb 2019 13:43:47 +0100 +Subject: brcmfmac: assure SSID length from firmware is limited + +The SSID length as received from firmware should not exceed +IEEE80211_MAX_SSID_LEN as that would result in heap overflow. + +Reviewed-by: Hante Meuleman +Reviewed-by: Pieter-Paul Giesberts +Reviewed-by: Franky Lin +Signed-off-by: Arend van Spriel +Signed-off-by: Kalle Valo +--- + drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +index b5e291ed9496c..012275fc3bf7b 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +@@ -3507,6 +3507,8 @@ brcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e, + } + + netinfo = brcmf_get_netinfo_array(pfn_result); ++ if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN) ++ netinfo->SSID_len = IEEE80211_MAX_SSID_LEN; + memcpy(cfg->wowl.nd->ssid.ssid, netinfo->SSID, netinfo->SSID_len); + cfg->wowl.nd->ssid.ssid_len = netinfo->SSID_len; + cfg->wowl.nd->n_channels = 1; +-- +cgit diff --git a/tests/cases/CVE-2019-9503.patch b/tests/cases/CVE-2019-9503.patch new file mode 100644 index 0000000..75b0f89 --- /dev/null +++ b/tests/cases/CVE-2019-9503.patch @@ -0,0 +1,104 @@ +From a4176ec356c73a46c07c181c6d04039fafa34a9f Mon Sep 17 00:00:00 2001 +From: Arend van Spriel +Date: Thu, 14 Feb 2019 13:43:48 +0100 +Subject: brcmfmac: add subtype check for event handling in data path + +For USB there is no separate channel being used to pass events +from firmware to the host driver and as such are passed over the +data path. In order to detect mock event messages an additional +check is needed on event subtype. This check is added conditionally +using unlikely() keyword. + +Reviewed-by: Hante Meuleman +Reviewed-by: Pieter-Paul Giesberts +Reviewed-by: Franky Lin +Signed-off-by: Arend van Spriel +Signed-off-by: Kalle Valo +--- + drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 5 +++-- + drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h | 16 ++++++++++++---- + .../net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c | 2 +- + 3 files changed, 16 insertions(+), 7 deletions(-) + +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +index e772c0845638a..a368ba6e7344f 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +@@ -519,7 +519,8 @@ void brcmf_rx_frame(struct device *dev, struct sk_buff *skb, bool handle_event) + } else { + /* Process special event packets */ + if (handle_event) +- brcmf_fweh_process_skb(ifp->drvr, skb); ++ brcmf_fweh_process_skb(ifp->drvr, skb, ++ BCMILCP_SUBTYPE_VENDOR_LONG); + + brcmf_netif_rx(ifp, skb); + } +@@ -536,7 +537,7 @@ void brcmf_rx_event(struct device *dev, struct sk_buff *skb) + if (brcmf_rx_hdrpull(drvr, skb, &ifp)) + return; + +- brcmf_fweh_process_skb(ifp->drvr, skb); ++ brcmf_fweh_process_skb(ifp->drvr, skb, 0); + brcmu_pkt_buf_free_skb(skb); + } + +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h +index 31f3e8e83a21e..7027243db17e3 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h +@@ -211,7 +211,7 @@ enum brcmf_fweh_event_code { + */ + #define BRCM_OUI "\x00\x10\x18" + #define BCMILCP_BCM_SUBTYPE_EVENT 1 +- ++#define BCMILCP_SUBTYPE_VENDOR_LONG 32769 + + /** + * struct brcm_ethhdr - broadcom specific ether header. +@@ -334,10 +334,10 @@ void brcmf_fweh_process_event(struct brcmf_pub *drvr, + void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing); + + static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr, +- struct sk_buff *skb) ++ struct sk_buff *skb, u16 stype) + { + struct brcmf_event *event_packet; +- u16 usr_stype; ++ u16 subtype, usr_stype; + + /* only process events when protocol matches */ + if (skb->protocol != cpu_to_be16(ETH_P_LINK_CTL)) +@@ -346,8 +346,16 @@ static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr, + if ((skb->len + ETH_HLEN) < sizeof(*event_packet)) + return; + +- /* check for BRCM oui match */ + event_packet = (struct brcmf_event *)skb_mac_header(skb); ++ ++ /* check subtype if needed */ ++ if (unlikely(stype)) { ++ subtype = get_unaligned_be16(&event_packet->hdr.subtype); ++ if (subtype != stype) ++ return; ++ } ++ ++ /* check for BRCM oui match */ + if (memcmp(BRCM_OUI, &event_packet->hdr.oui[0], + sizeof(event_packet->hdr.oui))) + return; +diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c +index 4e8397a0cbc8e..ee922b0525610 100644 +--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c ++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c +@@ -1116,7 +1116,7 @@ static void brcmf_msgbuf_process_event(struct brcmf_msgbuf *msgbuf, void *buf) + + skb->protocol = eth_type_trans(skb, ifp->ndev); + +- brcmf_fweh_process_skb(ifp->drvr, skb); ++ brcmf_fweh_process_skb(ifp->drvr, skb, 0); + + exit: + brcmu_pkt_buf_free_skb(skb); +-- +cgit diff --git a/tests/cases/CVE-2020-12351.patch b/tests/cases/CVE-2020-12351.patch new file mode 100644 index 0000000..ea7a402 --- /dev/null +++ b/tests/cases/CVE-2020-12351.patch @@ -0,0 +1,83 @@ +From f19425641cb2572a33cb074d5e30283720bd4d22 Mon Sep 17 00:00:00 2001 +From: Luiz Augusto von Dentz +Date: Thu, 6 Aug 2020 11:17:12 -0700 +Subject: Bluetooth: L2CAP: Fix calling sk_filter on non-socket based channel + +Only sockets will have the chan->data set to an actual sk, channels +like A2MP would have its own data which would likely cause a crash when +calling sk_filter, in order to fix this a new callback has been +introduced so channels can implement their own filtering if necessary. + +Signed-off-by: Luiz Augusto von Dentz +Signed-off-by: Marcel Holtmann +--- + include/net/bluetooth/l2cap.h | 2 ++ + net/bluetooth/l2cap_core.c | 7 ++++--- + net/bluetooth/l2cap_sock.c | 14 ++++++++++++++ + 3 files changed, 20 insertions(+), 3 deletions(-) + +diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h +index 8f1e6a7a2df84..1d1232917de72 100644 +--- a/include/net/bluetooth/l2cap.h ++++ b/include/net/bluetooth/l2cap.h +@@ -665,6 +665,8 @@ struct l2cap_ops { + struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan, + unsigned long hdr_len, + unsigned long len, int nb); ++ int (*filter) (struct l2cap_chan * chan, ++ struct sk_buff *skb); + }; + + struct l2cap_conn { +diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c +index ade83e2245670..1ab27b90ddcbc 100644 +--- a/net/bluetooth/l2cap_core.c ++++ b/net/bluetooth/l2cap_core.c +@@ -7301,9 +7301,10 @@ static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) + goto drop; + } + +- if ((chan->mode == L2CAP_MODE_ERTM || +- chan->mode == L2CAP_MODE_STREAMING) && sk_filter(chan->data, skb)) +- goto drop; ++ if (chan->ops->filter) { ++ if (chan->ops->filter(chan, skb)) ++ goto drop; ++ } + + if (!control->sframe) { + int err; +diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c +index e7cfe28140c39..f1b1edd0b6974 100644 +--- a/net/bluetooth/l2cap_sock.c ++++ b/net/bluetooth/l2cap_sock.c +@@ -1664,6 +1664,19 @@ static void l2cap_sock_suspend_cb(struct l2cap_chan *chan) + sk->sk_state_change(sk); + } + ++static int l2cap_sock_filter(struct l2cap_chan *chan, struct sk_buff *skb) ++{ ++ struct sock *sk = chan->data; ++ ++ switch (chan->mode) { ++ case L2CAP_MODE_ERTM: ++ case L2CAP_MODE_STREAMING: ++ return sk_filter(sk, skb); ++ } ++ ++ return 0; ++} ++ + static const struct l2cap_ops l2cap_chan_ops = { + .name = "L2CAP Socket Interface", + .new_connection = l2cap_sock_new_connection_cb, +@@ -1679,6 +1692,7 @@ static const struct l2cap_ops l2cap_chan_ops = { + .get_sndtimeo = l2cap_sock_get_sndtimeo_cb, + .get_peer_pid = l2cap_sock_get_peer_pid_cb, + .alloc_skb = l2cap_sock_alloc_skb_cb, ++ .filter = l2cap_sock_filter, + }; + + static void l2cap_sock_destruct(struct sock *sk) +-- +cgit diff --git a/tests/cases/CVE-2020-14381.patch b/tests/cases/CVE-2020-14381.patch new file mode 100644 index 0000000..ba57fed --- /dev/null +++ b/tests/cases/CVE-2020-14381.patch @@ -0,0 +1,226 @@ +From 8019ad13ef7f64be44d4f892af9c840179009254 Mon Sep 17 00:00:00 2001 +From: Peter Zijlstra +Date: Wed, 4 Mar 2020 11:28:31 +0100 +Subject: futex: Fix inode life-time issue + +As reported by Jann, ihold() does not in fact guarantee inode +persistence. And instead of making it so, replace the usage of inode +pointers with a per boot, machine wide, unique inode identifier. + +This sequence number is global, but shared (file backed) futexes are +rare enough that this should not become a performance issue. + +Reported-by: Jann Horn +Suggested-by: Linus Torvalds +Signed-off-by: Peter Zijlstra (Intel) +--- + fs/inode.c | 1 + + include/linux/fs.h | 1 + + include/linux/futex.h | 17 ++++++---- + kernel/futex.c | 89 ++++++++++++++++++++++++++++++--------------------- + 4 files changed, 65 insertions(+), 43 deletions(-) + +diff --git a/fs/inode.c b/fs/inode.c +index 7d57068b6b7ae..93d9252a00ab4 100644 +--- a/fs/inode.c ++++ b/fs/inode.c +@@ -138,6 +138,7 @@ int inode_init_always(struct super_block *sb, struct inode *inode) + inode->i_sb = sb; + inode->i_blkbits = sb->s_blocksize_bits; + inode->i_flags = 0; ++ atomic64_set(&inode->i_sequence, 0); + atomic_set(&inode->i_count, 1); + inode->i_op = &empty_iops; + inode->i_fop = &no_open_fops; +diff --git a/include/linux/fs.h b/include/linux/fs.h +index 3cd4fe6b845e7..abedbffe2c9e4 100644 +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -698,6 +698,7 @@ struct inode { + struct rcu_head i_rcu; + }; + atomic64_t i_version; ++ atomic64_t i_sequence; /* see futex */ + atomic_t i_count; + atomic_t i_dio_count; + atomic_t i_writecount; +diff --git a/include/linux/futex.h b/include/linux/futex.h +index 5cc3fed27d4c2..b70df27d7e85c 100644 +--- a/include/linux/futex.h ++++ b/include/linux/futex.h +@@ -31,23 +31,26 @@ struct task_struct; + + union futex_key { + struct { ++ u64 i_seq; + unsigned long pgoff; +- struct inode *inode; +- int offset; ++ unsigned int offset; + } shared; + struct { ++ union { ++ struct mm_struct *mm; ++ u64 __tmp; ++ }; + unsigned long address; +- struct mm_struct *mm; +- int offset; ++ unsigned int offset; + } private; + struct { ++ u64 ptr; + unsigned long word; +- void *ptr; +- int offset; ++ unsigned int offset; + } both; + }; + +-#define FUTEX_KEY_INIT (union futex_key) { .both = { .ptr = NULL } } ++#define FUTEX_KEY_INIT (union futex_key) { .both = { .ptr = 0ULL } } + + #ifdef CONFIG_FUTEX + enum { +diff --git a/kernel/futex.c b/kernel/futex.c +index 0cf84c8664f20..e14f7cd45dbd6 100644 +--- a/kernel/futex.c ++++ b/kernel/futex.c +@@ -429,7 +429,7 @@ static void get_futex_key_refs(union futex_key *key) + + switch (key->both.offset & (FUT_OFF_INODE|FUT_OFF_MMSHARED)) { + case FUT_OFF_INODE: +- ihold(key->shared.inode); /* implies smp_mb(); (B) */ ++ smp_mb(); /* explicit smp_mb(); (B) */ + break; + case FUT_OFF_MMSHARED: + futex_get_mm(key); /* implies smp_mb(); (B) */ +@@ -463,7 +463,6 @@ static void drop_futex_key_refs(union futex_key *key) + + switch (key->both.offset & (FUT_OFF_INODE|FUT_OFF_MMSHARED)) { + case FUT_OFF_INODE: +- iput(key->shared.inode); + break; + case FUT_OFF_MMSHARED: + mmdrop(key->private.mm); +@@ -505,6 +504,46 @@ futex_setup_timer(ktime_t *time, struct hrtimer_sleeper *timeout, + return timeout; + } + ++/* ++ * Generate a machine wide unique identifier for this inode. ++ * ++ * This relies on u64 not wrapping in the life-time of the machine; which with ++ * 1ns resolution means almost 585 years. ++ * ++ * This further relies on the fact that a well formed program will not unmap ++ * the file while it has a (shared) futex waiting on it. This mapping will have ++ * a file reference which pins the mount and inode. ++ * ++ * If for some reason an inode gets evicted and read back in again, it will get ++ * a new sequence number and will _NOT_ match, even though it is the exact same ++ * file. ++ * ++ * It is important that match_futex() will never have a false-positive, esp. ++ * for PI futexes that can mess up the state. The above argues that false-negatives ++ * are only possible for malformed programs. ++ */ ++static u64 get_inode_sequence_number(struct inode *inode) ++{ ++ static atomic64_t i_seq; ++ u64 old; ++ ++ /* Does the inode already have a sequence number? */ ++ old = atomic64_read(&inode->i_sequence); ++ if (likely(old)) ++ return old; ++ ++ for (;;) { ++ u64 new = atomic64_add_return(1, &i_seq); ++ if (WARN_ON_ONCE(!new)) ++ continue; ++ ++ old = atomic64_cmpxchg_relaxed(&inode->i_sequence, 0, new); ++ if (old) ++ return old; ++ return new; ++ } ++} ++ + /** + * get_futex_key() - Get parameters which are the keys for a futex + * @uaddr: virtual address of the futex +@@ -517,9 +556,15 @@ futex_setup_timer(ktime_t *time, struct hrtimer_sleeper *timeout, + * + * The key words are stored in @key on success. + * +- * For shared mappings, it's (page->index, file_inode(vma->vm_file), +- * offset_within_page). For private mappings, it's (uaddr, current->mm). +- * We can usually work out the index without swapping in the page. ++ * For shared mappings (when @fshared), the key is: ++ * ( inode->i_sequence, page->index, offset_within_page ) ++ * [ also see get_inode_sequence_number() ] ++ * ++ * For private mappings (or when !@fshared), the key is: ++ * ( current->mm, address, 0 ) ++ * ++ * This allows (cross process, where applicable) identification of the futex ++ * without keeping the page pinned for the duration of the FUTEX_WAIT. + * + * lock_page() might sleep, the caller should not hold a spinlock. + */ +@@ -659,8 +704,6 @@ again: + key->private.mm = mm; + key->private.address = address; + +- get_futex_key_refs(key); /* implies smp_mb(); (B) */ +- + } else { + struct inode *inode; + +@@ -692,40 +735,14 @@ again: + goto again; + } + +- /* +- * Take a reference unless it is about to be freed. Previously +- * this reference was taken by ihold under the page lock +- * pinning the inode in place so i_lock was unnecessary. The +- * only way for this check to fail is if the inode was +- * truncated in parallel which is almost certainly an +- * application bug. In such a case, just retry. +- * +- * We are not calling into get_futex_key_refs() in file-backed +- * cases, therefore a successful atomic_inc return below will +- * guarantee that get_futex_key() will still imply smp_mb(); (B). +- */ +- if (!atomic_inc_not_zero(&inode->i_count)) { +- rcu_read_unlock(); +- put_page(page); +- +- goto again; +- } +- +- /* Should be impossible but lets be paranoid for now */ +- if (WARN_ON_ONCE(inode->i_mapping != mapping)) { +- err = -EFAULT; +- rcu_read_unlock(); +- iput(inode); +- +- goto out; +- } +- + key->both.offset |= FUT_OFF_INODE; /* inode-based key */ +- key->shared.inode = inode; ++ key->shared.i_seq = get_inode_sequence_number(inode); + key->shared.pgoff = basepage_index(tail); + rcu_read_unlock(); + } + ++ get_futex_key_refs(key); /* implies smp_mb(); (B) */ ++ + out: + put_page(page); + return err; +-- +cgit diff --git a/tests/cases/CVE-2020-14386.patch b/tests/cases/CVE-2020-14386.patch new file mode 100644 index 0000000..133dac3 --- /dev/null +++ b/tests/cases/CVE-2020-14386.patch @@ -0,0 +1,53 @@ +From acf69c946233259ab4d64f8869d4037a198c7f06 Mon Sep 17 00:00:00 2001 +From: Or Cohen +Date: Thu, 3 Sep 2020 21:05:28 -0700 +Subject: net/packet: fix overflow in tpacket_rcv + +Using tp_reserve to calculate netoff can overflow as +tp_reserve is unsigned int and netoff is unsigned short. + +This may lead to macoff receving a smaller value then +sizeof(struct virtio_net_hdr), and if po->has_vnet_hdr +is set, an out-of-bounds write will occur when +calling virtio_net_hdr_from_skb. + +The bug is fixed by converting netoff to unsigned int +and checking if it exceeds USHRT_MAX. + +This addresses CVE-2020-14386 + +Fixes: 8913336a7e8d ("packet: add PACKET_RESERVE sockopt") +Signed-off-by: Or Cohen +Signed-off-by: Eric Dumazet +Signed-off-by: Linus Torvalds +--- + net/packet/af_packet.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c +index da8254e680f94..2b33e977a9059 100644 +--- a/net/packet/af_packet.c ++++ b/net/packet/af_packet.c +@@ -2170,7 +2170,8 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, + int skb_len = skb->len; + unsigned int snaplen, res; + unsigned long status = TP_STATUS_USER; +- unsigned short macoff, netoff, hdrlen; ++ unsigned short macoff, hdrlen; ++ unsigned int netoff; + struct sk_buff *copy_skb = NULL; + struct timespec64 ts; + __u32 ts_status; +@@ -2239,6 +2240,10 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, + } + macoff = netoff - maclen; + } ++ if (netoff > USHRT_MAX) { ++ atomic_inc(&po->tp_drops); ++ goto drop_n_restore; ++ } + if (po->tp_version <= TPACKET_V2) { + if (macoff + snaplen > po->rx_ring.frame_size) { + if (po->copy_thresh && +-- +cgit diff --git a/tests/cases/CVE-2020-25221.patch b/tests/cases/CVE-2020-25221.patch new file mode 100644 index 0000000..c76af08 --- /dev/null +++ b/tests/cases/CVE-2020-25221.patch @@ -0,0 +1,80 @@ +From 9fa2dd946743ae6f30dc4830da19147bf100a7f2 Mon Sep 17 00:00:00 2001 +From: Dave Hansen +Date: Thu, 3 Sep 2020 13:40:28 -0700 +Subject: mm: fix pin vs. gup mismatch with gate pages + +Gate pages were missed when converting from get to pin_user_pages(). +This can lead to refcount imbalances. This is reliably and quickly +reproducible running the x86 selftests when vsyscall=emulate is enabled +(the default). Fix by using try_grab_page() with appropriate flags +passed. + +The long story: + +Today, pin_user_pages() and get_user_pages() are similar interfaces for +manipulating page reference counts. However, "pins" use a "bias" value +and manipulate the actual reference count by 1024 instead of 1 used by +plain "gets". + +That means that pin_user_pages() must be matched with unpin_user_pages() +and can't be mixed with a plain put_user_pages() or put_page(). + +Enter gate pages, like the vsyscall page. They are pages usually in the +kernel image, but which are mapped to userspace. Userspace is allowed +access to them, including interfaces using get/pin_user_pages(). The +refcount of these kernel pages is manipulated just like a normal user +page on the get/pin side so that the put/unpin side can work the same +for normal user pages or gate pages. + +get_gate_page() uses try_get_page() which only bumps the refcount by +1, not 1024, even if called in the pin_user_pages() path. If someone +pins a gate page, this happens: + + pin_user_pages() + get_gate_page() + try_get_page() // bump refcount +1 + ... some time later + unpin_user_pages() + page_ref_sub_and_test(page, 1024)) + +... and boom, we get a refcount off by 1023. This is reliably and +quickly reproducible running the x86 selftests when booted with +vsyscall=emulate (the default). The selftests use ptrace(), but I +suspect anything using pin_user_pages() on gate pages could hit this. + +To fix it, simply use try_grab_page() instead of try_get_page(), and +pass 'gup_flags' in so that FOLL_PIN can be respected. + +This bug traces back to the very beginning of the FOLL_PIN support in +commit 3faa52c03f44 ("mm/gup: track FOLL_PIN pages"), which showed up in +the 5.7 release. + +Signed-off-by: Dave Hansen +Fixes: 3faa52c03f44 ("mm/gup: track FOLL_PIN pages") +Reported-by: Peter Zijlstra +Reviewed-by: John Hubbard +Acked-by: Andy Lutomirski +Cc: x86@kernel.org +Cc: Jann Horn +Cc: Andrew Morton +Cc: Kirill A. Shutemov +Signed-off-by: Linus Torvalds +--- + mm/gup.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/mm/gup.c b/mm/gup.c +index 6f47697f8fb0b..0d8d76f10ac61 100644 +--- a/mm/gup.c ++++ b/mm/gup.c +@@ -843,7 +843,7 @@ static int get_gate_page(struct mm_struct *mm, unsigned long address, + goto unmap; + *page = pte_page(*pte); + } +- if (unlikely(!try_get_page(*page))) { ++ if (unlikely(!try_grab_page(*page, gup_flags))) { + ret = -ENOMEM; + goto unmap; + } +-- +cgit diff --git a/tests/cases/CVE-2020-25668.patch b/tests/cases/CVE-2020-25668.patch new file mode 100644 index 0000000..3592f03 --- /dev/null +++ b/tests/cases/CVE-2020-25668.patch @@ -0,0 +1,165 @@ +From 90bfdeef83f1d6c696039b6a917190dcbbad3220 Mon Sep 17 00:00:00 2001 +From: Linus Torvalds +Date: Mon, 26 Oct 2020 13:15:23 -0700 +Subject: tty: make FONTX ioctl use the tty pointer they were actually passed + +Some of the font tty ioctl's always used the current foreground VC for +their operations. Don't do that then. + +This fixes a data race on fg_console. + +Side note: both Michael Ellerman and Jiri Slaby point out that all these +ioctls are deprecated, and should probably have been removed long ago, +and everything seems to be using the KDFONTOP ioctl instead. + +In fact, Michael points out that it looks like busybox's loadfont +program seems to have switched over to using KDFONTOP exactly _because_ +of this bug (ahem.. 12 years ago ;-). + +Reported-by: Minh Yuan +Acked-by: Michael Ellerman +Acked-by: Jiri Slaby +Cc: Greg KH +Signed-off-by: Linus Torvalds +--- + drivers/tty/vt/vt_ioctl.c | 36 +++++++++++++++++++----------------- + 1 file changed, 19 insertions(+), 17 deletions(-) + +diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c +index 2321775ef0982..5f61b25a9aaa8 100644 +--- a/drivers/tty/vt/vt_ioctl.c ++++ b/drivers/tty/vt/vt_ioctl.c +@@ -484,7 +484,7 @@ static int vt_k_ioctl(struct tty_struct *tty, unsigned int cmd, + return 0; + } + +-static inline int do_fontx_ioctl(int cmd, ++static inline int do_fontx_ioctl(struct vc_data *vc, int cmd, + struct consolefontdesc __user *user_cfd, + struct console_font_op *op) + { +@@ -502,15 +502,16 @@ static inline int do_fontx_ioctl(int cmd, + op->height = cfdarg.charheight; + op->charcount = cfdarg.charcount; + op->data = cfdarg.chardata; +- return con_font_op(vc_cons[fg_console].d, op); +- case GIO_FONTX: { ++ return con_font_op(vc, op); ++ ++ case GIO_FONTX: + op->op = KD_FONT_OP_GET; + op->flags = KD_FONT_FLAG_OLD; + op->width = 8; + op->height = cfdarg.charheight; + op->charcount = cfdarg.charcount; + op->data = cfdarg.chardata; +- i = con_font_op(vc_cons[fg_console].d, op); ++ i = con_font_op(vc, op); + if (i) + return i; + cfdarg.charheight = op->height; +@@ -518,12 +519,11 @@ static inline int do_fontx_ioctl(int cmd, + if (copy_to_user(user_cfd, &cfdarg, sizeof(struct consolefontdesc))) + return -EFAULT; + return 0; +- } + } + return -EINVAL; + } + +-static int vt_io_fontreset(struct console_font_op *op) ++static int vt_io_fontreset(struct vc_data *vc, struct console_font_op *op) + { + int ret; + +@@ -537,12 +537,12 @@ static int vt_io_fontreset(struct console_font_op *op) + + op->op = KD_FONT_OP_SET_DEFAULT; + op->data = NULL; +- ret = con_font_op(vc_cons[fg_console].d, op); ++ ret = con_font_op(vc, op); + if (ret) + return ret; + + console_lock(); +- con_set_default_unimap(vc_cons[fg_console].d); ++ con_set_default_unimap(vc); + console_unlock(); + + return 0; +@@ -584,7 +584,7 @@ static int vt_io_ioctl(struct vc_data *vc, unsigned int cmd, void __user *up, + op.height = 0; + op.charcount = 256; + op.data = up; +- return con_font_op(vc_cons[fg_console].d, &op); ++ return con_font_op(vc, &op); + + case GIO_FONT: + op.op = KD_FONT_OP_GET; +@@ -593,7 +593,7 @@ static int vt_io_ioctl(struct vc_data *vc, unsigned int cmd, void __user *up, + op.height = 32; + op.charcount = 256; + op.data = up; +- return con_font_op(vc_cons[fg_console].d, &op); ++ return con_font_op(vc, &op); + + case PIO_CMAP: + if (!perm) +@@ -609,13 +609,13 @@ static int vt_io_ioctl(struct vc_data *vc, unsigned int cmd, void __user *up, + + fallthrough; + case GIO_FONTX: +- return do_fontx_ioctl(cmd, up, &op); ++ return do_fontx_ioctl(vc, cmd, up, &op); + + case PIO_FONTRESET: + if (!perm) + return -EPERM; + +- return vt_io_fontreset(&op); ++ return vt_io_fontreset(vc, &op); + + case PIO_SCRNMAP: + if (!perm) +@@ -1066,8 +1066,9 @@ struct compat_consolefontdesc { + }; + + static inline int +-compat_fontx_ioctl(int cmd, struct compat_consolefontdesc __user *user_cfd, +- int perm, struct console_font_op *op) ++compat_fontx_ioctl(struct vc_data *vc, int cmd, ++ struct compat_consolefontdesc __user *user_cfd, ++ int perm, struct console_font_op *op) + { + struct compat_consolefontdesc cfdarg; + int i; +@@ -1085,7 +1086,8 @@ compat_fontx_ioctl(int cmd, struct compat_consolefontdesc __user *user_cfd, + op->height = cfdarg.charheight; + op->charcount = cfdarg.charcount; + op->data = compat_ptr(cfdarg.chardata); +- return con_font_op(vc_cons[fg_console].d, op); ++ return con_font_op(vc, op); ++ + case GIO_FONTX: + op->op = KD_FONT_OP_GET; + op->flags = KD_FONT_FLAG_OLD; +@@ -1093,7 +1095,7 @@ compat_fontx_ioctl(int cmd, struct compat_consolefontdesc __user *user_cfd, + op->height = cfdarg.charheight; + op->charcount = cfdarg.charcount; + op->data = compat_ptr(cfdarg.chardata); +- i = con_font_op(vc_cons[fg_console].d, op); ++ i = con_font_op(vc, op); + if (i) + return i; + cfdarg.charheight = op->height; +@@ -1183,7 +1185,7 @@ long vt_compat_ioctl(struct tty_struct *tty, + */ + case PIO_FONTX: + case GIO_FONTX: +- return compat_fontx_ioctl(cmd, up, perm, &op); ++ return compat_fontx_ioctl(vc, cmd, up, perm, &op); + + case KDFONTOP: + return compat_kdfontop_ioctl(up, perm, &op, vc); +-- +cgit diff --git a/tests/cases/CVE-2020-25669.patch b/tests/cases/CVE-2020-25669.patch new file mode 100644 index 0000000..91eb624 --- /dev/null +++ b/tests/cases/CVE-2020-25669.patch @@ -0,0 +1,92 @@ +From 77e70d351db7de07a46ac49b87a6c3c7a60fca7e Mon Sep 17 00:00:00 2001 +From: Dmitry Torokhov +Date: Mon, 26 Oct 2020 13:36:17 -0700 +Subject: Input: sunkbd - avoid use-after-free in teardown paths + +We need to make sure we cancel the reinit work before we tear down the +driver structures. + +Reported-by: Bodong Zhao +Tested-by: Bodong Zhao +Cc: stable@vger.kernel.org +Signed-off-by: Dmitry Torokhov +--- + drivers/input/keyboard/sunkbd.c | 41 +++++++++++++++++++++++++++++++++-------- + 1 file changed, 33 insertions(+), 8 deletions(-) + +diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c +index 27126e621eb60..d450f11b98a70 100644 +--- a/drivers/input/keyboard/sunkbd.c ++++ b/drivers/input/keyboard/sunkbd.c +@@ -99,7 +99,8 @@ static irqreturn_t sunkbd_interrupt(struct serio *serio, + switch (data) { + + case SUNKBD_RET_RESET: +- schedule_work(&sunkbd->tq); ++ if (sunkbd->enabled) ++ schedule_work(&sunkbd->tq); + sunkbd->reset = -1; + break; + +@@ -200,16 +201,12 @@ static int sunkbd_initialize(struct sunkbd *sunkbd) + } + + /* +- * sunkbd_reinit() sets leds and beeps to a state the computer remembers they +- * were in. ++ * sunkbd_set_leds_beeps() sets leds and beeps to a state the computer remembers ++ * they were in. + */ + +-static void sunkbd_reinit(struct work_struct *work) ++static void sunkbd_set_leds_beeps(struct sunkbd *sunkbd) + { +- struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq); +- +- wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ); +- + serio_write(sunkbd->serio, SUNKBD_CMD_SETLED); + serio_write(sunkbd->serio, + (!!test_bit(LED_CAPSL, sunkbd->dev->led) << 3) | +@@ -222,11 +219,39 @@ static void sunkbd_reinit(struct work_struct *work) + SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd)); + } + ++ ++/* ++ * sunkbd_reinit() wait for the keyboard reset to complete and restores state ++ * of leds and beeps. ++ */ ++ ++static void sunkbd_reinit(struct work_struct *work) ++{ ++ struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq); ++ ++ /* ++ * It is OK that we check sunkbd->enabled without pausing serio, ++ * as we only want to catch true->false transition that will ++ * happen once and we will be woken up for it. ++ */ ++ wait_event_interruptible_timeout(sunkbd->wait, ++ sunkbd->reset >= 0 || !sunkbd->enabled, ++ HZ); ++ ++ if (sunkbd->reset >= 0 && sunkbd->enabled) ++ sunkbd_set_leds_beeps(sunkbd); ++} ++ + static void sunkbd_enable(struct sunkbd *sunkbd, bool enable) + { + serio_pause_rx(sunkbd->serio); + sunkbd->enabled = enable; + serio_continue_rx(sunkbd->serio); ++ ++ if (!enable) { ++ wake_up_interruptible(&sunkbd->wait); ++ cancel_work_sync(&sunkbd->tq); ++ } + } + + /* +-- +cgit diff --git a/tests/cases/CVE-2020-25705.patch b/tests/cases/CVE-2020-25705.patch new file mode 100644 index 0000000..c07dfbf --- /dev/null +++ b/tests/cases/CVE-2020-25705.patch @@ -0,0 +1,69 @@ +From b38e7819cae946e2edf869e604af1e65a5d241c5 Mon Sep 17 00:00:00 2001 +From: Eric Dumazet +Date: Thu, 15 Oct 2020 11:42:00 -0700 +Subject: icmp: randomize the global rate limiter + +Keyu Man reported that the ICMP rate limiter could be used +by attackers to get useful signal. Details will be provided +in an upcoming academic publication. + +Our solution is to add some noise, so that the attackers +no longer can get help from the predictable token bucket limiter. + +Fixes: 4cdf507d5452 ("icmp: add a global rate limitation") +Signed-off-by: Eric Dumazet +Reported-by: Keyu Man +Signed-off-by: Jakub Kicinski +--- + Documentation/networking/ip-sysctl.rst | 4 +++- + net/ipv4/icmp.c | 7 +++++-- + 2 files changed, 8 insertions(+), 3 deletions(-) + +diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst +index 837d51f9e1fab..25e6673a085a0 100644 +--- a/Documentation/networking/ip-sysctl.rst ++++ b/Documentation/networking/ip-sysctl.rst +@@ -1142,13 +1142,15 @@ icmp_ratelimit - INTEGER + icmp_msgs_per_sec - INTEGER + Limit maximal number of ICMP packets sent per second from this host. + Only messages whose type matches icmp_ratemask (see below) are +- controlled by this limit. ++ controlled by this limit. For security reasons, the precise count ++ of messages per second is randomized. + + Default: 1000 + + icmp_msgs_burst - INTEGER + icmp_msgs_per_sec controls number of ICMP packets sent per second, + while icmp_msgs_burst controls the burst size of these packets. ++ For security reasons, the precise burst size is randomized. + + Default: 50 + +diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c +index 07f67ced962a6..005faea415a48 100644 +--- a/net/ipv4/icmp.c ++++ b/net/ipv4/icmp.c +@@ -239,7 +239,7 @@ static struct { + /** + * icmp_global_allow - Are we allowed to send one more ICMP message ? + * +- * Uses a token bucket to limit our ICMP messages to sysctl_icmp_msgs_per_sec. ++ * Uses a token bucket to limit our ICMP messages to ~sysctl_icmp_msgs_per_sec. + * Returns false if we reached the limit and can not send another packet. + * Note: called with BH disabled + */ +@@ -267,7 +267,10 @@ bool icmp_global_allow(void) + } + credit = min_t(u32, icmp_global.credit + incr, sysctl_icmp_msgs_burst); + if (credit) { +- credit--; ++ /* We want to use a credit of one in average, but need to randomize ++ * it for security reasons. ++ */ ++ credit = max_t(int, credit - prandom_u32_max(3), 0); + rc = true; + } + WRITE_ONCE(icmp_global.credit, credit); +-- +cgit diff --git a/tests/cases/CVE-2020-27786.patch b/tests/cases/CVE-2020-27786.patch new file mode 100644 index 0000000..7a8bbd7 --- /dev/null +++ b/tests/cases/CVE-2020-27786.patch @@ -0,0 +1,131 @@ +From c1f6e3c818dd734c30f6a7eeebf232ba2cf3181d Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Thu, 7 May 2020 13:44:56 +0200 +Subject: ALSA: rawmidi: Fix racy buffer resize under concurrent accesses + +The rawmidi core allows user to resize the runtime buffer via ioctl, +and this may lead to UAF when performed during concurrent reads or +writes: the read/write functions unlock the runtime lock temporarily +during copying form/to user-space, and that's the race window. + +This patch fixes the hole by introducing a reference counter for the +runtime buffer read/write access and returns -EBUSY error when the +resize is performed concurrently against read/write. + +Note that the ref count field is a simple integer instead of +refcount_t here, since the all contexts accessing the buffer is +basically protected with a spinlock, hence we need no expensive atomic +ops. Also, note that this busy check is needed only against read / +write functions, and not in receive/transmit callbacks; the race can +happen only at the spinlock hole mentioned in the above, while the +whole function is protected for receive / transmit callbacks. + +Reported-by: butt3rflyh4ck +Cc: +Link: https://lore.kernel.org/r/CAFcO6XMWpUVK_yzzCpp8_XP7+=oUpQvuBeCbMffEDkpe8jWrfg@mail.gmail.com +Link: https://lore.kernel.org/r/s5heerw3r5z.wl-tiwai@suse.de +Signed-off-by: Takashi Iwai +--- + include/sound/rawmidi.h | 1 + + sound/core/rawmidi.c | 31 +++++++++++++++++++++++++++---- + 2 files changed, 28 insertions(+), 4 deletions(-) + +diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h +index a36b7227a15ad..334842daa9045 100644 +--- a/include/sound/rawmidi.h ++++ b/include/sound/rawmidi.h +@@ -61,6 +61,7 @@ struct snd_rawmidi_runtime { + size_t avail_min; /* min avail for wakeup */ + size_t avail; /* max used buffer for wakeup */ + size_t xruns; /* over/underruns counter */ ++ int buffer_ref; /* buffer reference count */ + /* misc */ + spinlock_t lock; + wait_queue_head_t sleep; +diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c +index 20dd08e1f6756..2a688b711a9ac 100644 +--- a/sound/core/rawmidi.c ++++ b/sound/core/rawmidi.c +@@ -120,6 +120,17 @@ static void snd_rawmidi_input_event_work(struct work_struct *work) + runtime->event(runtime->substream); + } + ++/* buffer refcount management: call with runtime->lock held */ ++static inline void snd_rawmidi_buffer_ref(struct snd_rawmidi_runtime *runtime) ++{ ++ runtime->buffer_ref++; ++} ++ ++static inline void snd_rawmidi_buffer_unref(struct snd_rawmidi_runtime *runtime) ++{ ++ runtime->buffer_ref--; ++} ++ + static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream) + { + struct snd_rawmidi_runtime *runtime; +@@ -669,6 +680,11 @@ static int resize_runtime_buffer(struct snd_rawmidi_runtime *runtime, + if (!newbuf) + return -ENOMEM; + spin_lock_irq(&runtime->lock); ++ if (runtime->buffer_ref) { ++ spin_unlock_irq(&runtime->lock); ++ kvfree(newbuf); ++ return -EBUSY; ++ } + oldbuf = runtime->buffer; + runtime->buffer = newbuf; + runtime->buffer_size = params->buffer_size; +@@ -1019,8 +1035,10 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream, + long result = 0, count1; + struct snd_rawmidi_runtime *runtime = substream->runtime; + unsigned long appl_ptr; ++ int err = 0; + + spin_lock_irqsave(&runtime->lock, flags); ++ snd_rawmidi_buffer_ref(runtime); + while (count > 0 && runtime->avail) { + count1 = runtime->buffer_size - runtime->appl_ptr; + if (count1 > count) +@@ -1039,16 +1057,19 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream, + if (userbuf) { + spin_unlock_irqrestore(&runtime->lock, flags); + if (copy_to_user(userbuf + result, +- runtime->buffer + appl_ptr, count1)) { +- return result > 0 ? result : -EFAULT; +- } ++ runtime->buffer + appl_ptr, count1)) ++ err = -EFAULT; + spin_lock_irqsave(&runtime->lock, flags); ++ if (err) ++ goto out; + } + result += count1; + count -= count1; + } ++ out: ++ snd_rawmidi_buffer_unref(runtime); + spin_unlock_irqrestore(&runtime->lock, flags); +- return result; ++ return result > 0 ? result : err; + } + + long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream, +@@ -1342,6 +1363,7 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, + return -EAGAIN; + } + } ++ snd_rawmidi_buffer_ref(runtime); + while (count > 0 && runtime->avail > 0) { + count1 = runtime->buffer_size - runtime->appl_ptr; + if (count1 > count) +@@ -1373,6 +1395,7 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, + } + __end: + count1 = runtime->avail < runtime->buffer_size; ++ snd_rawmidi_buffer_unref(runtime); + spin_unlock_irqrestore(&runtime->lock, flags); + if (count1) + snd_rawmidi_output_trigger(substream, 1); +-- +cgit diff --git a/tests/cases/CVE-2020-28374.patch b/tests/cases/CVE-2020-28374.patch new file mode 100644 index 0000000..c58747b --- /dev/null +++ b/tests/cases/CVE-2020-28374.patch @@ -0,0 +1,208 @@ +From 2896c93811e39d63a4d9b63ccf12a8fbc226e5e4 Mon Sep 17 00:00:00 2001 +From: David Disseldorp +Date: Tue, 3 Nov 2020 02:21:58 +0100 +Subject: scsi: target: Fix XCOPY NAA identifier lookup + +When attempting to match EXTENDED COPY CSCD descriptors with corresponding +se_devices, target_xcopy_locate_se_dev_e4() currently iterates over LIO's +global devices list which includes all configured backstores. + +This change ensures that only initiator-accessible backstores are +considered during CSCD descriptor lookup, according to the session's +se_node_acl LUN list. + +To avoid LUN removal race conditions, device pinning is changed from being +configfs based to instead using the se_node_acl lun_ref. + +Reference: CVE-2020-28374 +Fixes: cbf031f425fd ("target: Add support for EXTENDED_COPY copy offload emulation") +Reviewed-by: Lee Duncan +Signed-off-by: David Disseldorp +Signed-off-by: Mike Christie +Signed-off-by: Martin K. Petersen +--- + drivers/target/target_core_xcopy.c | 119 ++++++++++++++++++++++--------------- + drivers/target/target_core_xcopy.h | 1 + + 2 files changed, 71 insertions(+), 49 deletions(-) + +diff --git a/drivers/target/target_core_xcopy.c b/drivers/target/target_core_xcopy.c +index 44e15d7fb2f09..66d6f1d06f219 100644 +--- a/drivers/target/target_core_xcopy.c ++++ b/drivers/target/target_core_xcopy.c +@@ -46,60 +46,83 @@ static int target_xcopy_gen_naa_ieee(struct se_device *dev, unsigned char *buf) + return 0; + } + +-struct xcopy_dev_search_info { +- const unsigned char *dev_wwn; +- struct se_device *found_dev; +-}; +- ++/** ++ * target_xcopy_locate_se_dev_e4_iter - compare XCOPY NAA device identifiers ++ * ++ * @se_dev: device being considered for match ++ * @dev_wwn: XCOPY requested NAA dev_wwn ++ * @return: 1 on match, 0 on no-match ++ */ + static int target_xcopy_locate_se_dev_e4_iter(struct se_device *se_dev, +- void *data) ++ const unsigned char *dev_wwn) + { +- struct xcopy_dev_search_info *info = data; + unsigned char tmp_dev_wwn[XCOPY_NAA_IEEE_REGEX_LEN]; + int rc; + +- if (!se_dev->dev_attrib.emulate_3pc) ++ if (!se_dev->dev_attrib.emulate_3pc) { ++ pr_debug("XCOPY: emulate_3pc disabled on se_dev %p\n", se_dev); + return 0; ++ } + + memset(&tmp_dev_wwn[0], 0, XCOPY_NAA_IEEE_REGEX_LEN); + target_xcopy_gen_naa_ieee(se_dev, &tmp_dev_wwn[0]); + +- rc = memcmp(&tmp_dev_wwn[0], info->dev_wwn, XCOPY_NAA_IEEE_REGEX_LEN); +- if (rc != 0) +- return 0; +- +- info->found_dev = se_dev; +- pr_debug("XCOPY 0xe4: located se_dev: %p\n", se_dev); +- +- rc = target_depend_item(&se_dev->dev_group.cg_item); ++ rc = memcmp(&tmp_dev_wwn[0], dev_wwn, XCOPY_NAA_IEEE_REGEX_LEN); + if (rc != 0) { +- pr_err("configfs_depend_item attempt failed: %d for se_dev: %p\n", +- rc, se_dev); +- return rc; ++ pr_debug("XCOPY: skip non-matching: %*ph\n", ++ XCOPY_NAA_IEEE_REGEX_LEN, tmp_dev_wwn); ++ return 0; + } ++ pr_debug("XCOPY 0xe4: located se_dev: %p\n", se_dev); + +- pr_debug("Called configfs_depend_item for se_dev: %p se_dev->se_dev_group: %p\n", +- se_dev, &se_dev->dev_group); + return 1; + } + +-static int target_xcopy_locate_se_dev_e4(const unsigned char *dev_wwn, +- struct se_device **found_dev) ++static int target_xcopy_locate_se_dev_e4(struct se_session *sess, ++ const unsigned char *dev_wwn, ++ struct se_device **_found_dev, ++ struct percpu_ref **_found_lun_ref) + { +- struct xcopy_dev_search_info info; +- int ret; +- +- memset(&info, 0, sizeof(info)); +- info.dev_wwn = dev_wwn; +- +- ret = target_for_each_device(target_xcopy_locate_se_dev_e4_iter, &info); +- if (ret == 1) { +- *found_dev = info.found_dev; +- return 0; +- } else { +- pr_debug_ratelimited("Unable to locate 0xe4 descriptor for EXTENDED_COPY\n"); +- return -EINVAL; ++ struct se_dev_entry *deve; ++ struct se_node_acl *nacl; ++ struct se_lun *this_lun = NULL; ++ struct se_device *found_dev = NULL; ++ ++ /* cmd with NULL sess indicates no associated $FABRIC_MOD */ ++ if (!sess) ++ goto err_out; ++ ++ pr_debug("XCOPY 0xe4: searching for: %*ph\n", ++ XCOPY_NAA_IEEE_REGEX_LEN, dev_wwn); ++ ++ nacl = sess->se_node_acl; ++ rcu_read_lock(); ++ hlist_for_each_entry_rcu(deve, &nacl->lun_entry_hlist, link) { ++ struct se_device *this_dev; ++ int rc; ++ ++ this_lun = rcu_dereference(deve->se_lun); ++ this_dev = rcu_dereference_raw(this_lun->lun_se_dev); ++ ++ rc = target_xcopy_locate_se_dev_e4_iter(this_dev, dev_wwn); ++ if (rc) { ++ if (percpu_ref_tryget_live(&this_lun->lun_ref)) ++ found_dev = this_dev; ++ break; ++ } + } ++ rcu_read_unlock(); ++ if (found_dev == NULL) ++ goto err_out; ++ ++ pr_debug("lun_ref held for se_dev: %p se_dev->se_dev_group: %p\n", ++ found_dev, &found_dev->dev_group); ++ *_found_dev = found_dev; ++ *_found_lun_ref = &this_lun->lun_ref; ++ return 0; ++err_out: ++ pr_debug_ratelimited("Unable to locate 0xe4 descriptor for EXTENDED_COPY\n"); ++ return -EINVAL; + } + + static int target_xcopy_parse_tiddesc_e4(struct se_cmd *se_cmd, struct xcopy_op *xop, +@@ -246,12 +269,16 @@ static int target_xcopy_parse_target_descriptors(struct se_cmd *se_cmd, + + switch (xop->op_origin) { + case XCOL_SOURCE_RECV_OP: +- rc = target_xcopy_locate_se_dev_e4(xop->dst_tid_wwn, +- &xop->dst_dev); ++ rc = target_xcopy_locate_se_dev_e4(se_cmd->se_sess, ++ xop->dst_tid_wwn, ++ &xop->dst_dev, ++ &xop->remote_lun_ref); + break; + case XCOL_DEST_RECV_OP: +- rc = target_xcopy_locate_se_dev_e4(xop->src_tid_wwn, +- &xop->src_dev); ++ rc = target_xcopy_locate_se_dev_e4(se_cmd->se_sess, ++ xop->src_tid_wwn, ++ &xop->src_dev, ++ &xop->remote_lun_ref); + break; + default: + pr_err("XCOPY CSCD descriptor IDs not found in CSCD list - " +@@ -391,18 +418,12 @@ static int xcopy_pt_get_cmd_state(struct se_cmd *se_cmd) + + static void xcopy_pt_undepend_remotedev(struct xcopy_op *xop) + { +- struct se_device *remote_dev; +- + if (xop->op_origin == XCOL_SOURCE_RECV_OP) +- remote_dev = xop->dst_dev; ++ pr_debug("putting dst lun_ref for %p\n", xop->dst_dev); + else +- remote_dev = xop->src_dev; +- +- pr_debug("Calling configfs_undepend_item for" +- " remote_dev: %p remote_dev->dev_group: %p\n", +- remote_dev, &remote_dev->dev_group.cg_item); ++ pr_debug("putting src lun_ref for %p\n", xop->src_dev); + +- target_undepend_item(&remote_dev->dev_group.cg_item); ++ percpu_ref_put(xop->remote_lun_ref); + } + + static void xcopy_pt_release_cmd(struct se_cmd *se_cmd) +diff --git a/drivers/target/target_core_xcopy.h b/drivers/target/target_core_xcopy.h +index c56a1bde9417b..e5f20005179a8 100644 +--- a/drivers/target/target_core_xcopy.h ++++ b/drivers/target/target_core_xcopy.h +@@ -27,6 +27,7 @@ struct xcopy_op { + struct se_device *dst_dev; + unsigned char dst_tid_wwn[XCOPY_NAA_IEEE_REGEX_LEN]; + unsigned char local_dev_wwn[XCOPY_NAA_IEEE_REGEX_LEN]; ++ struct percpu_ref *remote_lun_ref; + + sector_t src_lba; + sector_t dst_lba; +-- +cgit diff --git a/tests/cases/CVE-2020-29368.patch b/tests/cases/CVE-2020-29368.patch new file mode 100644 index 0000000..164c93b --- /dev/null +++ b/tests/cases/CVE-2020-29368.patch @@ -0,0 +1,101 @@ +From c444eb564fb16645c172d550359cb3d75fe8a040 Mon Sep 17 00:00:00 2001 +From: Andrea Arcangeli +Date: Wed, 27 May 2020 19:06:24 -0400 +Subject: mm: thp: make the THP mapcount atomic against + __split_huge_pmd_locked() + +Write protect anon page faults require an accurate mapcount to decide +if to break the COW or not. This is implemented in the THP path with +reuse_swap_page() -> +page_trans_huge_map_swapcount()/page_trans_huge_mapcount(). + +If the COW triggers while the other processes sharing the page are +under a huge pmd split, to do an accurate reading, we must ensure the +mapcount isn't computed while it's being transferred from the head +page to the tail pages. + +reuse_swap_cache() already runs serialized by the page lock, so it's +enough to add the page lock around __split_huge_pmd_locked too, in +order to add the missing serialization. + +Note: the commit in "Fixes" is just to facilitate the backporting, +because the code before such commit didn't try to do an accurate THP +mapcount calculation and it instead used the page_count() to decide if +to COW or not. Both the page_count and the pin_count are THP-wide +refcounts, so they're inaccurate if used in +reuse_swap_page(). Reverting such commit (besides the unrelated fix to +the local anon_vma assignment) would have also opened the window for +memory corruption side effects to certain workloads as documented in +such commit header. + +Signed-off-by: Andrea Arcangeli +Suggested-by: Jann Horn +Reported-by: Jann Horn +Acked-by: Kirill A. Shutemov +Fixes: 6d0a07edd17c ("mm: thp: calculate the mapcount correctly for THP pages during WP faults") +Cc: stable@vger.kernel.org +Signed-off-by: Linus Torvalds +--- + mm/huge_memory.c | 31 ++++++++++++++++++++++++++++--- + 1 file changed, 28 insertions(+), 3 deletions(-) + +diff --git a/mm/huge_memory.c b/mm/huge_memory.c +index 11fe0b4dbe673..dddc863b3cbcf 100644 +--- a/mm/huge_memory.c ++++ b/mm/huge_memory.c +@@ -2385,6 +2385,8 @@ void __split_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd, + { + spinlock_t *ptl; + struct mmu_notifier_range range; ++ bool was_locked = false; ++ pmd_t _pmd; + + mmu_notifier_range_init(&range, MMU_NOTIFY_CLEAR, 0, vma, vma->vm_mm, + address & HPAGE_PMD_MASK, +@@ -2397,11 +2399,32 @@ void __split_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd, + * pmd against. Otherwise we can end up replacing wrong page. + */ + VM_BUG_ON(freeze && !page); +- if (page && page != pmd_page(*pmd)) +- goto out; ++ if (page) { ++ VM_WARN_ON_ONCE(!PageLocked(page)); ++ was_locked = true; ++ if (page != pmd_page(*pmd)) ++ goto out; ++ } + ++repeat: + if (pmd_trans_huge(*pmd)) { +- page = pmd_page(*pmd); ++ if (!page) { ++ page = pmd_page(*pmd); ++ if (unlikely(!trylock_page(page))) { ++ get_page(page); ++ _pmd = *pmd; ++ spin_unlock(ptl); ++ lock_page(page); ++ spin_lock(ptl); ++ if (unlikely(!pmd_same(*pmd, _pmd))) { ++ unlock_page(page); ++ put_page(page); ++ page = NULL; ++ goto repeat; ++ } ++ put_page(page); ++ } ++ } + if (PageMlocked(page)) + clear_page_mlock(page); + } else if (!(pmd_devmap(*pmd) || is_pmd_migration_entry(*pmd))) +@@ -2409,6 +2432,8 @@ void __split_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd, + __split_huge_pmd_locked(vma, pmd, range.start, freeze); + out: + spin_unlock(ptl); ++ if (!was_locked && page) ++ unlock_page(page); + /* + * No need to double call mmu_notifier->invalidate_range() callback. + * They are 3 cases to consider inside __split_huge_pmd_locked(): +-- +cgit diff --git a/tests/cases/CVE-2020-29369.patch b/tests/cases/CVE-2020-29369.patch new file mode 100644 index 0000000..2e8b795 --- /dev/null +++ b/tests/cases/CVE-2020-29369.patch @@ -0,0 +1,88 @@ +From 246c320a8cfe0b11d81a4af38fa9985ef0cc9a4c Mon Sep 17 00:00:00 2001 +From: "Kirill A. Shutemov" +Date: Thu, 23 Jul 2020 21:15:11 -0700 +Subject: mm/mmap.c: close race between munmap() and + expand_upwards()/downwards() + +VMA with VM_GROWSDOWN or VM_GROWSUP flag set can change their size under +mmap_read_lock(). It can lead to race with __do_munmap(): + + Thread A Thread B +__do_munmap() + detach_vmas_to_be_unmapped() + mmap_write_downgrade() + expand_downwards() + vma->vm_start = address; + // The VMA now overlaps with + // VMAs detached by the Thread A + // page fault populates expanded part + // of the VMA + unmap_region() + // Zaps pagetables partly + // populated by Thread B + +Similar race exists for expand_upwards(). + +The fix is to avoid downgrading mmap_lock in __do_munmap() if detached +VMAs are next to VM_GROWSDOWN or VM_GROWSUP VMA. + +[akpm@linux-foundation.org: s/mmap_sem/mmap_lock/ in comment] + +Fixes: dd2283f2605e ("mm: mmap: zap pages with read mmap_sem in munmap") +Reported-by: Jann Horn +Signed-off-by: Kirill A. Shutemov +Signed-off-by: Andrew Morton +Reviewed-by: Yang Shi +Acked-by: Vlastimil Babka +Cc: Oleg Nesterov +Cc: Matthew Wilcox +Cc: [4.20+] +Link: http://lkml.kernel.org/r/20200709105309.42495-1-kirill.shutemov@linux.intel.com +Signed-off-by: Linus Torvalds +--- + mm/mmap.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/mm/mmap.c b/mm/mmap.c +index 59a4682ebf3fa..8c7ca737a19b3 100644 +--- a/mm/mmap.c ++++ b/mm/mmap.c +@@ -2620,7 +2620,7 @@ static void unmap_region(struct mm_struct *mm, + * Create a list of vma's touched by the unmap, removing them from the mm's + * vma list as we go.. + */ +-static void ++static bool + detach_vmas_to_be_unmapped(struct mm_struct *mm, struct vm_area_struct *vma, + struct vm_area_struct *prev, unsigned long end) + { +@@ -2645,6 +2645,17 @@ detach_vmas_to_be_unmapped(struct mm_struct *mm, struct vm_area_struct *vma, + + /* Kill the cache */ + vmacache_invalidate(mm); ++ ++ /* ++ * Do not downgrade mmap_lock if we are next to VM_GROWSDOWN or ++ * VM_GROWSUP VMA. Such VMAs can change their size under ++ * down_read(mmap_lock) and collide with the VMA we are about to unmap. ++ */ ++ if (vma && (vma->vm_flags & VM_GROWSDOWN)) ++ return false; ++ if (prev && (prev->vm_flags & VM_GROWSUP)) ++ return false; ++ return true; + } + + /* +@@ -2825,7 +2836,8 @@ int __do_munmap(struct mm_struct *mm, unsigned long start, size_t len, + } + + /* Detach vmas from rbtree */ +- detach_vmas_to_be_unmapped(mm, vma, prev, end); ++ if (!detach_vmas_to_be_unmapped(mm, vma, prev, end)) ++ downgrade = false; + + if (downgrade) + mmap_write_downgrade(mm); +-- +cgit diff --git a/tests/cases/CVE-2020-29370.patch b/tests/cases/CVE-2020-29370.patch new file mode 100644 index 0000000..faf7d11 --- /dev/null +++ b/tests/cases/CVE-2020-29370.patch @@ -0,0 +1,44 @@ +From fd4d9c7d0c71866ec0c2825189ebd2ce35bd95b8 Mon Sep 17 00:00:00 2001 +From: Jann Horn +Date: Tue, 17 Mar 2020 01:28:45 +0100 +Subject: mm: slub: add missing TID bump in kmem_cache_alloc_bulk() + +When kmem_cache_alloc_bulk() attempts to allocate N objects from a percpu +freelist of length M, and N > M > 0, it will first remove the M elements +from the percpu freelist, then call ___slab_alloc() to allocate the next +element and repopulate the percpu freelist. ___slab_alloc() can re-enable +IRQs via allocate_slab(), so the TID must be bumped before ___slab_alloc() +to properly commit the freelist head change. + +Fix it by unconditionally bumping c->tid when entering the slowpath. + +Cc: stable@vger.kernel.org +Fixes: ebe909e0fdb3 ("slub: improve bulk alloc strategy") +Signed-off-by: Jann Horn +Signed-off-by: Linus Torvalds +--- + mm/slub.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/mm/slub.c b/mm/slub.c +index 17dc00e33115b..eae5bb47b22f1 100644 +--- a/mm/slub.c ++++ b/mm/slub.c +@@ -3174,6 +3174,15 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size, + void *object = c->freelist; + + if (unlikely(!object)) { ++ /* ++ * We may have removed an object from c->freelist using ++ * the fastpath in the previous iteration; in that case, ++ * c->tid has not been bumped yet. ++ * Since ___slab_alloc() may reenable interrupts while ++ * allocating memory, we should bump c->tid now. ++ */ ++ c->tid = next_tid(c->tid); ++ + /* + * Invoking slow path likely have side-effect + * of re-populating per CPU c->freelist +-- +cgit diff --git a/tests/cases/CVE-2020-29373.patch b/tests/cases/CVE-2020-29373.patch new file mode 100644 index 0000000..8109368 --- /dev/null +++ b/tests/cases/CVE-2020-29373.patch @@ -0,0 +1,110 @@ +From ff002b30181d30cdfbca316dadd099c3ca0d739c Mon Sep 17 00:00:00 2001 +From: Jens Axboe +Date: Fri, 7 Feb 2020 16:05:21 -0700 +Subject: io_uring: grab ->fs as part of async preparation + +This passes it in to io-wq, so it assumes the right fs_struct when +executing async work that may need to do lookups. + +Cc: stable@vger.kernel.org # 5.3+ +Signed-off-by: Jens Axboe +--- + fs/io_uring.c | 28 ++++++++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +diff --git a/fs/io_uring.c b/fs/io_uring.c +index 1a3ca6577a101..2a7bb178986e0 100644 +--- a/fs/io_uring.c ++++ b/fs/io_uring.c +@@ -75,6 +75,7 @@ + #include + #include + #include ++#include + + #define CREATE_TRACE_POINTS + #include +@@ -611,6 +612,8 @@ struct io_op_def { + unsigned not_supported : 1; + /* needs file table */ + unsigned file_table : 1; ++ /* needs ->fs */ ++ unsigned needs_fs : 1; + }; + + static const struct io_op_def io_op_defs[] = { +@@ -653,12 +656,14 @@ static const struct io_op_def io_op_defs[] = { + .needs_mm = 1, + .needs_file = 1, + .unbound_nonreg_file = 1, ++ .needs_fs = 1, + }, + [IORING_OP_RECVMSG] = { + .async_ctx = 1, + .needs_mm = 1, + .needs_file = 1, + .unbound_nonreg_file = 1, ++ .needs_fs = 1, + }, + [IORING_OP_TIMEOUT] = { + .async_ctx = 1, +@@ -689,6 +694,7 @@ static const struct io_op_def io_op_defs[] = { + .needs_file = 1, + .fd_non_neg = 1, + .file_table = 1, ++ .needs_fs = 1, + }, + [IORING_OP_CLOSE] = { + .needs_file = 1, +@@ -702,6 +708,7 @@ static const struct io_op_def io_op_defs[] = { + .needs_mm = 1, + .needs_file = 1, + .fd_non_neg = 1, ++ .needs_fs = 1, + }, + [IORING_OP_READ] = { + .needs_mm = 1, +@@ -733,6 +740,7 @@ static const struct io_op_def io_op_defs[] = { + .needs_file = 1, + .fd_non_neg = 1, + .file_table = 1, ++ .needs_fs = 1, + }, + [IORING_OP_EPOLL_CTL] = { + .unbound_nonreg_file = 1, +@@ -907,6 +915,16 @@ static inline void io_req_work_grab_env(struct io_kiocb *req, + } + if (!req->work.creds) + req->work.creds = get_current_cred(); ++ if (!req->work.fs && def->needs_fs) { ++ spin_lock(¤t->fs->lock); ++ if (!current->fs->in_exec) { ++ req->work.fs = current->fs; ++ req->work.fs->users++; ++ } else { ++ req->work.flags |= IO_WQ_WORK_CANCEL; ++ } ++ spin_unlock(¤t->fs->lock); ++ } + } + + static inline void io_req_work_drop_env(struct io_kiocb *req) +@@ -919,6 +937,16 @@ static inline void io_req_work_drop_env(struct io_kiocb *req) + put_cred(req->work.creds); + req->work.creds = NULL; + } ++ if (req->work.fs) { ++ struct fs_struct *fs = req->work.fs; ++ ++ spin_lock(&req->work.fs->lock); ++ if (--fs->users) ++ fs = NULL; ++ spin_unlock(&req->work.fs->lock); ++ if (fs) ++ free_fs_struct(fs); ++ } + } + + static inline bool io_prep_async_work(struct io_kiocb *req, +-- +cgit diff --git a/tests/cases/CVE-2020-29534.patch b/tests/cases/CVE-2020-29534.patch new file mode 100644 index 0000000..a98a756 --- /dev/null +++ b/tests/cases/CVE-2020-29534.patch @@ -0,0 +1,680 @@ +From 0f2122045b946241a9e549c2a76cea54fa58a7ff Mon Sep 17 00:00:00 2001 +From: Jens Axboe +Date: Sun, 13 Sep 2020 13:09:39 -0600 +Subject: io_uring: don't rely on weak ->files references + +Grab actual references to the files_struct. To avoid circular references +issues due to this, we add a per-task note that keeps track of what +io_uring contexts a task has used. When the tasks execs or exits its +assigned files, we cancel requests based on this tracking. + +With that, we can grab proper references to the files table, and no +longer need to rely on stashing away ring_fd and ring_file to check +if the ring_fd may have been closed. + +Cc: stable@vger.kernel.org # v5.5+ +Reviewed-by: Pavel Begunkov +Signed-off-by: Jens Axboe +--- + fs/exec.c | 6 + + fs/file.c | 2 + + fs/io_uring.c | 306 +++++++++++++++++++++++++++++++++++++++++------ + include/linux/io_uring.h | 53 ++++++++ + include/linux/sched.h | 5 + + init/init_task.c | 3 + + kernel/fork.c | 6 + + 7 files changed, 344 insertions(+), 37 deletions(-) + create mode 100644 include/linux/io_uring.h + +diff --git a/fs/exec.c b/fs/exec.c +index a91003e28eaae..07910f5032e74 100644 +--- a/fs/exec.c ++++ b/fs/exec.c +@@ -62,6 +62,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -1895,6 +1896,11 @@ static int bprm_execve(struct linux_binprm *bprm, + struct files_struct *displaced; + int retval; + ++ /* ++ * Cancel any io_uring activity across execve ++ */ ++ io_uring_task_cancel(); ++ + retval = unshare_files(&displaced); + if (retval) + return retval; +diff --git a/fs/file.c b/fs/file.c +index 21c0893f2f1df..4559b5fec3bd5 100644 +--- a/fs/file.c ++++ b/fs/file.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + + unsigned int sysctl_nr_open __read_mostly = 1024*1024; + unsigned int sysctl_nr_open_min = BITS_PER_LONG; +@@ -452,6 +453,7 @@ void exit_files(struct task_struct *tsk) + struct files_struct * files = tsk->files; + + if (files) { ++ io_uring_files_cancel(files); + task_lock(tsk); + tsk->files = NULL; + task_unlock(tsk); +diff --git a/fs/io_uring.c b/fs/io_uring.c +index 046d06266a116..ee75ba7113cfe 100644 +--- a/fs/io_uring.c ++++ b/fs/io_uring.c +@@ -79,6 +79,7 @@ + #include + #include + #include ++#include + + #define CREATE_TRACE_POINTS + #include +@@ -284,8 +285,6 @@ struct io_ring_ctx { + */ + struct fixed_file_data *file_data; + unsigned nr_user_files; +- int ring_fd; +- struct file *ring_file; + + /* if used, fixed mapped user buffers */ + unsigned nr_user_bufs; +@@ -1433,7 +1432,12 @@ static void __io_cqring_fill_event(struct io_kiocb *req, long res, long cflags) + WRITE_ONCE(cqe->user_data, req->user_data); + WRITE_ONCE(cqe->res, res); + WRITE_ONCE(cqe->flags, cflags); +- } else if (ctx->cq_overflow_flushed) { ++ } else if (ctx->cq_overflow_flushed || req->task->io_uring->in_idle) { ++ /* ++ * If we're in ring overflow flush mode, or in task cancel mode, ++ * then we cannot store the request for later flushing, we need ++ * to drop it on the floor. ++ */ + WRITE_ONCE(ctx->rings->cq_overflow, + atomic_inc_return(&ctx->cached_cq_overflow)); + } else { +@@ -1591,8 +1595,12 @@ static bool io_dismantle_req(struct io_kiocb *req) + + static void __io_free_req_finish(struct io_kiocb *req) + { ++ struct io_uring_task *tctx = req->task->io_uring; + struct io_ring_ctx *ctx = req->ctx; + ++ atomic_long_inc(&tctx->req_complete); ++ if (tctx->in_idle) ++ wake_up(&tctx->wait); + put_task_struct(req->task); + + if (likely(!io_is_fallback_req(req))) +@@ -1907,6 +1915,7 @@ static void io_req_free_batch_finish(struct io_ring_ctx *ctx, + if (rb->to_free) + __io_req_free_batch_flush(ctx, rb); + if (rb->task) { ++ atomic_long_add(rb->task_refs, &rb->task->io_uring->req_complete); + put_task_struct_many(rb->task, rb->task_refs); + rb->task = NULL; + } +@@ -1922,8 +1931,10 @@ static void io_req_free_batch(struct req_batch *rb, struct io_kiocb *req) + io_queue_next(req); + + if (req->task != rb->task) { +- if (rb->task) ++ if (rb->task) { ++ atomic_long_add(rb->task_refs, &rb->task->io_uring->req_complete); + put_task_struct_many(rb->task, rb->task_refs); ++ } + rb->task = req->task; + rb->task_refs = 0; + } +@@ -3978,8 +3989,7 @@ static int io_close_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) + return -EBADF; + + req->close.fd = READ_ONCE(sqe->fd); +- if ((req->file && req->file->f_op == &io_uring_fops) || +- req->close.fd == req->ctx->ring_fd) ++ if ((req->file && req->file->f_op == &io_uring_fops)) + return -EBADF; + + req->close.put_file = NULL; +@@ -5667,6 +5677,7 @@ static void io_req_drop_files(struct io_kiocb *req) + wake_up(&ctx->inflight_wait); + spin_unlock_irqrestore(&ctx->inflight_lock, flags); + req->flags &= ~REQ_F_INFLIGHT; ++ put_files_struct(req->work.files); + req->work.files = NULL; + } + +@@ -6067,34 +6078,20 @@ static int io_req_set_file(struct io_submit_state *state, struct io_kiocb *req, + + static int io_grab_files(struct io_kiocb *req) + { +- int ret = -EBADF; + struct io_ring_ctx *ctx = req->ctx; + + io_req_init_async(req); + + if (req->work.files || (req->flags & REQ_F_NO_FILE_TABLE)) + return 0; +- if (!ctx->ring_file) +- return -EBADF; + +- rcu_read_lock(); ++ req->work.files = get_files_struct(current); ++ req->flags |= REQ_F_INFLIGHT; ++ + spin_lock_irq(&ctx->inflight_lock); +- /* +- * We use the f_ops->flush() handler to ensure that we can flush +- * out work accessing these files if the fd is closed. Check if +- * the fd has changed since we started down this path, and disallow +- * this operation if it has. +- */ +- if (fcheck(ctx->ring_fd) == ctx->ring_file) { +- list_add(&req->inflight_entry, &ctx->inflight_list); +- req->flags |= REQ_F_INFLIGHT; +- req->work.files = current->files; +- ret = 0; +- } ++ list_add(&req->inflight_entry, &ctx->inflight_list); + spin_unlock_irq(&ctx->inflight_lock); +- rcu_read_unlock(); +- +- return ret; ++ return 0; + } + + static inline int io_prep_work_files(struct io_kiocb *req) +@@ -6459,6 +6456,7 @@ static int io_init_req(struct io_ring_ctx *ctx, struct io_kiocb *req, + refcount_set(&req->refs, 2); + req->task = current; + get_task_struct(req->task); ++ atomic_long_inc(&req->task->io_uring->req_issue); + req->result = 0; + + if (unlikely(req->opcode >= IORING_OP_LAST)) +@@ -6494,8 +6492,7 @@ static int io_init_req(struct io_ring_ctx *ctx, struct io_kiocb *req, + return io_req_set_file(state, req, READ_ONCE(sqe->fd)); + } + +-static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr, +- struct file *ring_file, int ring_fd) ++static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr) + { + struct io_submit_state state; + struct io_kiocb *link = NULL; +@@ -6516,9 +6513,6 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr, + + io_submit_state_start(&state, ctx, nr); + +- ctx->ring_fd = ring_fd; +- ctx->ring_file = ring_file; +- + for (i = 0; i < nr; i++) { + const struct io_uring_sqe *sqe; + struct io_kiocb *req; +@@ -6687,7 +6681,7 @@ static int io_sq_thread(void *data) + + mutex_lock(&ctx->uring_lock); + if (likely(!percpu_ref_is_dying(&ctx->refs))) +- ret = io_submit_sqes(ctx, to_submit, NULL, -1); ++ ret = io_submit_sqes(ctx, to_submit); + mutex_unlock(&ctx->uring_lock); + timeout = jiffies + ctx->sq_thread_idle; + } +@@ -7516,6 +7510,34 @@ out_fput: + return ret; + } + ++static int io_uring_alloc_task_context(struct task_struct *task) ++{ ++ struct io_uring_task *tctx; ++ ++ tctx = kmalloc(sizeof(*tctx), GFP_KERNEL); ++ if (unlikely(!tctx)) ++ return -ENOMEM; ++ ++ xa_init(&tctx->xa); ++ init_waitqueue_head(&tctx->wait); ++ tctx->last = NULL; ++ tctx->in_idle = 0; ++ atomic_long_set(&tctx->req_issue, 0); ++ atomic_long_set(&tctx->req_complete, 0); ++ task->io_uring = tctx; ++ return 0; ++} ++ ++void __io_uring_free(struct task_struct *tsk) ++{ ++ struct io_uring_task *tctx = tsk->io_uring; ++ ++ WARN_ON_ONCE(!xa_empty(&tctx->xa)); ++ xa_destroy(&tctx->xa); ++ kfree(tctx); ++ tsk->io_uring = NULL; ++} ++ + static int io_sq_offload_start(struct io_ring_ctx *ctx, + struct io_uring_params *p) + { +@@ -7551,6 +7573,9 @@ static int io_sq_offload_start(struct io_ring_ctx *ctx, + ctx->sqo_thread = NULL; + goto err; + } ++ ret = io_uring_alloc_task_context(ctx->sqo_thread); ++ if (ret) ++ goto err; + wake_up_process(ctx->sqo_thread); + } else if (p->flags & IORING_SETUP_SQ_AFF) { + /* Can't have SQ_AFF without SQPOLL */ +@@ -8063,7 +8088,7 @@ static bool io_wq_files_match(struct io_wq_work *work, void *data) + { + struct files_struct *files = data; + +- return work->files == files; ++ return !files || work->files == files; + } + + /* +@@ -8218,7 +8243,7 @@ static bool io_uring_cancel_files(struct io_ring_ctx *ctx, + + spin_lock_irq(&ctx->inflight_lock); + list_for_each_entry(req, &ctx->inflight_list, inflight_entry) { +- if (req->work.files != files) ++ if (files && req->work.files != files) + continue; + /* req is being completed, ignore */ + if (!refcount_inc_not_zero(&req->refs)) +@@ -8254,18 +8279,217 @@ static bool io_cancel_task_cb(struct io_wq_work *work, void *data) + return io_task_match(req, task); + } + ++static bool __io_uring_cancel_task_requests(struct io_ring_ctx *ctx, ++ struct task_struct *task, ++ struct files_struct *files) ++{ ++ bool ret; ++ ++ ret = io_uring_cancel_files(ctx, files); ++ if (!files) { ++ enum io_wq_cancel cret; ++ ++ cret = io_wq_cancel_cb(ctx->io_wq, io_cancel_task_cb, task, true); ++ if (cret != IO_WQ_CANCEL_NOTFOUND) ++ ret = true; ++ ++ /* SQPOLL thread does its own polling */ ++ if (!(ctx->flags & IORING_SETUP_SQPOLL)) { ++ while (!list_empty_careful(&ctx->iopoll_list)) { ++ io_iopoll_try_reap_events(ctx); ++ ret = true; ++ } ++ } ++ ++ ret |= io_poll_remove_all(ctx, task); ++ ret |= io_kill_timeouts(ctx, task); ++ } ++ ++ return ret; ++} ++ ++/* ++ * We need to iteratively cancel requests, in case a request has dependent ++ * hard links. These persist even for failure of cancelations, hence keep ++ * looping until none are found. ++ */ ++static void io_uring_cancel_task_requests(struct io_ring_ctx *ctx, ++ struct files_struct *files) ++{ ++ struct task_struct *task = current; ++ ++ if (ctx->flags & IORING_SETUP_SQPOLL) ++ task = ctx->sqo_thread; ++ ++ io_cqring_overflow_flush(ctx, true, task, files); ++ ++ while (__io_uring_cancel_task_requests(ctx, task, files)) { ++ io_run_task_work(); ++ cond_resched(); ++ } ++} ++ ++/* ++ * Note that this task has used io_uring. We use it for cancelation purposes. ++ */ ++static int io_uring_add_task_file(struct file *file) ++{ ++ if (unlikely(!current->io_uring)) { ++ int ret; ++ ++ ret = io_uring_alloc_task_context(current); ++ if (unlikely(ret)) ++ return ret; ++ } ++ if (current->io_uring->last != file) { ++ XA_STATE(xas, ¤t->io_uring->xa, (unsigned long) file); ++ void *old; ++ ++ rcu_read_lock(); ++ old = xas_load(&xas); ++ if (old != file) { ++ get_file(file); ++ xas_lock(&xas); ++ xas_store(&xas, file); ++ xas_unlock(&xas); ++ } ++ rcu_read_unlock(); ++ current->io_uring->last = file; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Remove this io_uring_file -> task mapping. ++ */ ++static void io_uring_del_task_file(struct file *file) ++{ ++ struct io_uring_task *tctx = current->io_uring; ++ XA_STATE(xas, &tctx->xa, (unsigned long) file); ++ ++ if (tctx->last == file) ++ tctx->last = NULL; ++ ++ xas_lock(&xas); ++ file = xas_store(&xas, NULL); ++ xas_unlock(&xas); ++ ++ if (file) ++ fput(file); ++} ++ ++static void __io_uring_attempt_task_drop(struct file *file) ++{ ++ XA_STATE(xas, ¤t->io_uring->xa, (unsigned long) file); ++ struct file *old; ++ ++ rcu_read_lock(); ++ old = xas_load(&xas); ++ rcu_read_unlock(); ++ ++ if (old == file) ++ io_uring_del_task_file(file); ++} ++ ++/* ++ * Drop task note for this file if we're the only ones that hold it after ++ * pending fput() ++ */ ++static void io_uring_attempt_task_drop(struct file *file, bool exiting) ++{ ++ if (!current->io_uring) ++ return; ++ /* ++ * fput() is pending, will be 2 if the only other ref is our potential ++ * task file note. If the task is exiting, drop regardless of count. ++ */ ++ if (!exiting && atomic_long_read(&file->f_count) != 2) ++ return; ++ ++ __io_uring_attempt_task_drop(file); ++} ++ ++void __io_uring_files_cancel(struct files_struct *files) ++{ ++ struct io_uring_task *tctx = current->io_uring; ++ XA_STATE(xas, &tctx->xa, 0); ++ ++ /* make sure overflow events are dropped */ ++ tctx->in_idle = true; ++ ++ do { ++ struct io_ring_ctx *ctx; ++ struct file *file; ++ ++ xas_lock(&xas); ++ file = xas_next_entry(&xas, ULONG_MAX); ++ xas_unlock(&xas); ++ ++ if (!file) ++ break; ++ ++ ctx = file->private_data; ++ ++ io_uring_cancel_task_requests(ctx, files); ++ if (files) ++ io_uring_del_task_file(file); ++ } while (1); ++} ++ ++static inline bool io_uring_task_idle(struct io_uring_task *tctx) ++{ ++ return atomic_long_read(&tctx->req_issue) == ++ atomic_long_read(&tctx->req_complete); ++} ++ ++/* ++ * Find any io_uring fd that this task has registered or done IO on, and cancel ++ * requests. ++ */ ++void __io_uring_task_cancel(void) ++{ ++ struct io_uring_task *tctx = current->io_uring; ++ DEFINE_WAIT(wait); ++ long completions; ++ ++ /* make sure overflow events are dropped */ ++ tctx->in_idle = true; ++ ++ while (!io_uring_task_idle(tctx)) { ++ /* read completions before cancelations */ ++ completions = atomic_long_read(&tctx->req_complete); ++ __io_uring_files_cancel(NULL); ++ ++ prepare_to_wait(&tctx->wait, &wait, TASK_UNINTERRUPTIBLE); ++ ++ /* ++ * If we've seen completions, retry. This avoids a race where ++ * a completion comes in before we did prepare_to_wait(). ++ */ ++ if (completions != atomic_long_read(&tctx->req_complete)) ++ continue; ++ if (io_uring_task_idle(tctx)) ++ break; ++ schedule(); ++ } ++ ++ finish_wait(&tctx->wait, &wait); ++ tctx->in_idle = false; ++} ++ + static int io_uring_flush(struct file *file, void *data) + { + struct io_ring_ctx *ctx = file->private_data; + +- io_uring_cancel_files(ctx, data); +- + /* + * If the task is going away, cancel work it may have pending + */ + if (fatal_signal_pending(current) || (current->flags & PF_EXITING)) +- io_wq_cancel_cb(ctx->io_wq, io_cancel_task_cb, current, true); ++ data = NULL; + ++ io_uring_cancel_task_requests(ctx, data); ++ io_uring_attempt_task_drop(file, !data); + return 0; + } + +@@ -8379,8 +8603,11 @@ SYSCALL_DEFINE6(io_uring_enter, unsigned int, fd, u32, to_submit, + wake_up(&ctx->sqo_wait); + submitted = to_submit; + } else if (to_submit) { ++ ret = io_uring_add_task_file(f.file); ++ if (unlikely(ret)) ++ goto out; + mutex_lock(&ctx->uring_lock); +- submitted = io_submit_sqes(ctx, to_submit, f.file, fd); ++ submitted = io_submit_sqes(ctx, to_submit); + mutex_unlock(&ctx->uring_lock); + + if (submitted != to_submit) +@@ -8590,6 +8817,7 @@ static int io_uring_get_fd(struct io_ring_ctx *ctx) + file = anon_inode_getfile("[io_uring]", &io_uring_fops, ctx, + O_RDWR | O_CLOEXEC); + if (IS_ERR(file)) { ++err_fd: + put_unused_fd(ret); + ret = PTR_ERR(file); + goto err; +@@ -8598,6 +8826,10 @@ static int io_uring_get_fd(struct io_ring_ctx *ctx) + #if defined(CONFIG_UNIX) + ctx->ring_sock->file = file; + #endif ++ if (unlikely(io_uring_add_task_file(file))) { ++ file = ERR_PTR(-ENOMEM); ++ goto err_fd; ++ } + fd_install(ret, file); + return ret; + err: +diff --git a/include/linux/io_uring.h b/include/linux/io_uring.h +new file mode 100644 +index 0000000000000..c09135a1ef132 +--- /dev/null ++++ b/include/linux/io_uring.h +@@ -0,0 +1,53 @@ ++/* SPDX-License-Identifier: GPL-2.0-or-later */ ++#ifndef _LINUX_IO_URING_H ++#define _LINUX_IO_URING_H ++ ++#include ++#include ++#include ++ ++struct io_uring_task { ++ /* submission side */ ++ struct xarray xa; ++ struct wait_queue_head wait; ++ struct file *last; ++ atomic_long_t req_issue; ++ ++ /* completion side */ ++ bool in_idle ____cacheline_aligned_in_smp; ++ atomic_long_t req_complete; ++}; ++ ++#if defined(CONFIG_IO_URING) ++void __io_uring_task_cancel(void); ++void __io_uring_files_cancel(struct files_struct *files); ++void __io_uring_free(struct task_struct *tsk); ++ ++static inline void io_uring_task_cancel(void) ++{ ++ if (current->io_uring && !xa_empty(¤t->io_uring->xa)) ++ __io_uring_task_cancel(); ++} ++static inline void io_uring_files_cancel(struct files_struct *files) ++{ ++ if (current->io_uring && !xa_empty(¤t->io_uring->xa)) ++ __io_uring_files_cancel(files); ++} ++static inline void io_uring_free(struct task_struct *tsk) ++{ ++ if (tsk->io_uring) ++ __io_uring_free(tsk); ++} ++#else ++static inline void io_uring_task_cancel(void) ++{ ++} ++static inline void io_uring_files_cancel(struct files_struct *files) ++{ ++} ++static inline void io_uring_free(struct task_struct *tsk) ++{ ++} ++#endif ++ ++#endif +diff --git a/include/linux/sched.h b/include/linux/sched.h +index afe01e232935f..8bf2295ebee48 100644 +--- a/include/linux/sched.h ++++ b/include/linux/sched.h +@@ -63,6 +63,7 @@ struct sighand_struct; + struct signal_struct; + struct task_delay_info; + struct task_group; ++struct io_uring_task; + + /* + * Task state bitmask. NOTE! These bits are also +@@ -935,6 +936,10 @@ struct task_struct { + /* Open file information: */ + struct files_struct *files; + ++#ifdef CONFIG_IO_URING ++ struct io_uring_task *io_uring; ++#endif ++ + /* Namespaces: */ + struct nsproxy *nsproxy; + +diff --git a/init/init_task.c b/init/init_task.c +index f6889fce64af7..a56f0abb63e93 100644 +--- a/init/init_task.c ++++ b/init/init_task.c +@@ -114,6 +114,9 @@ struct task_struct init_task + .thread = INIT_THREAD, + .fs = &init_fs, + .files = &init_files, ++#ifdef CONFIG_IO_URING ++ .io_uring = NULL, ++#endif + .signal = &init_signals, + .sighand = &init_sighand, + .nsproxy = &init_nsproxy, +diff --git a/kernel/fork.c b/kernel/fork.c +index da8d360fb0326..a3795aaaab5c5 100644 +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -95,6 +95,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -728,6 +729,7 @@ void __put_task_struct(struct task_struct *tsk) + WARN_ON(refcount_read(&tsk->usage)); + WARN_ON(tsk == current); + ++ io_uring_free(tsk); + cgroup_free(tsk); + task_numa_free(tsk, true); + security_task_free(tsk); +@@ -1983,6 +1985,10 @@ static __latent_entropy struct task_struct *copy_process( + p->vtime.state = VTIME_INACTIVE; + #endif + ++#ifdef CONFIG_IO_URING ++ p->io_uring = NULL; ++#endif ++ + #if defined(SPLIT_RSS_COUNTING) + memset(&p->rss_stat, 0, sizeof(p->rss_stat)); + #endif +-- +cgit diff --git a/tests/cases/CVE-2020-29661.patch b/tests/cases/CVE-2020-29661.patch new file mode 100644 index 0000000..113a810 --- /dev/null +++ b/tests/cases/CVE-2020-29661.patch @@ -0,0 +1,43 @@ +From 54ffccbf053b5b6ca4f6e45094b942fab92a25fc Mon Sep 17 00:00:00 2001 +From: Jann Horn +Date: Thu, 3 Dec 2020 02:25:04 +0100 +Subject: tty: Fix ->pgrp locking in tiocspgrp() + +tiocspgrp() takes two tty_struct pointers: One to the tty that userspace +passed to ioctl() (`tty`) and one to the TTY being changed (`real_tty`). +These pointers are different when ioctl() is called with a master fd. + +To properly lock real_tty->pgrp, we must take real_tty->ctrl_lock. + +This bug makes it possible for racing ioctl(TIOCSPGRP, ...) calls on +both sides of a PTY pair to corrupt the refcount of `struct pid`, +leading to use-after-free errors. + +Fixes: 47f86834bbd4 ("redo locking of tty->pgrp") +CC: stable@kernel.org +Signed-off-by: Jann Horn +Reviewed-by: Jiri Slaby +Signed-off-by: Greg Kroah-Hartman +--- + drivers/tty/tty_jobctrl.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/tty/tty_jobctrl.c b/drivers/tty/tty_jobctrl.c +index 28a23a0fef21c..baadeea4a289b 100644 +--- a/drivers/tty/tty_jobctrl.c ++++ b/drivers/tty/tty_jobctrl.c +@@ -494,10 +494,10 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t + if (session_of_pgrp(pgrp) != task_session(current)) + goto out_unlock; + retval = 0; +- spin_lock_irq(&tty->ctrl_lock); ++ spin_lock_irq(&real_tty->ctrl_lock); + put_pid(real_tty->pgrp); + real_tty->pgrp = get_pid(pgrp); +- spin_unlock_irq(&tty->ctrl_lock); ++ spin_unlock_irq(&real_tty->ctrl_lock); + out_unlock: + rcu_read_unlock(); + return retval; +-- +cgit diff --git a/tests/cases/CVE-2020-36385.patch b/tests/cases/CVE-2020-36385.patch new file mode 100644 index 0000000..7e2b8f7 --- /dev/null +++ b/tests/cases/CVE-2020-36385.patch @@ -0,0 +1,177 @@ +From f5449e74802c1112dea984aec8af7a33c4516af1 Mon Sep 17 00:00:00 2001 +From: Jason Gunthorpe +Date: Mon, 14 Sep 2020 08:59:56 -0300 +Subject: RDMA/ucma: Rework ucma_migrate_id() to avoid races with destroy + +ucma_destroy_id() assumes that all things accessing the ctx will do so via +the xarray. This assumption violated only in the case the FD is being +closed, then the ctx is reached via the ctx_list. Normally this is OK +since ucma_destroy_id() cannot run concurrenty with release(), however +with ucma_migrate_id() is involved this can violated as the close of the +2nd FD can run concurrently with destroy on the first: + + CPU0 CPU1 + ucma_destroy_id(fda) + ucma_migrate_id(fda -> fdb) + ucma_get_ctx() + xa_lock() + _ucma_find_context() + xa_erase() + xa_unlock() + xa_lock() + ctx->file = new_file + list_move() + xa_unlock() + ucma_put_ctx() + + ucma_close(fdb) + _destroy_id() + kfree(ctx) + + _destroy_id() + wait_for_completion() + // boom, ctx was freed + +The ctx->file must be modified under the handler and xa_lock, and prior to +modification the ID must be rechecked that it is still reachable from +cur_file, ie there is no parallel destroy or migrate. + +To make this work remove the double locking and streamline the control +flow. The double locking was obsoleted by the handler lock now directly +preventing new uevents from being created, and the ctx_list cannot be read +while holding fgets on both files. Removing the double locking also +removes the need to check for the same file. + +Fixes: 88314e4dda1e ("RDMA/cma: add support for rdma_migrate_id()") +Link: https://lore.kernel.org/r/0-v1-05c5a4090305+3a872-ucma_syz_migrate_jgg@nvidia.com +Reported-and-tested-by: syzbot+cc6fc752b3819e082d0c@syzkaller.appspotmail.com +Signed-off-by: Jason Gunthorpe +--- + drivers/infiniband/core/ucma.c | 79 ++++++++++++++++-------------------------- + 1 file changed, 29 insertions(+), 50 deletions(-) + +diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c +index a5595bd489b08..b3a7dbb12f259 100644 +--- a/drivers/infiniband/core/ucma.c ++++ b/drivers/infiniband/core/ucma.c +@@ -1587,45 +1587,15 @@ out: + return ret; + } + +-static void ucma_lock_files(struct ucma_file *file1, struct ucma_file *file2) +-{ +- /* Acquire mutex's based on pointer comparison to prevent deadlock. */ +- if (file1 < file2) { +- mutex_lock(&file1->mut); +- mutex_lock_nested(&file2->mut, SINGLE_DEPTH_NESTING); +- } else { +- mutex_lock(&file2->mut); +- mutex_lock_nested(&file1->mut, SINGLE_DEPTH_NESTING); +- } +-} +- +-static void ucma_unlock_files(struct ucma_file *file1, struct ucma_file *file2) +-{ +- if (file1 < file2) { +- mutex_unlock(&file2->mut); +- mutex_unlock(&file1->mut); +- } else { +- mutex_unlock(&file1->mut); +- mutex_unlock(&file2->mut); +- } +-} +- +-static void ucma_move_events(struct ucma_context *ctx, struct ucma_file *file) +-{ +- struct ucma_event *uevent, *tmp; +- +- list_for_each_entry_safe(uevent, tmp, &ctx->file->event_list, list) +- if (uevent->ctx == ctx) +- list_move_tail(&uevent->list, &file->event_list); +-} +- + static ssize_t ucma_migrate_id(struct ucma_file *new_file, + const char __user *inbuf, + int in_len, int out_len) + { + struct rdma_ucm_migrate_id cmd; + struct rdma_ucm_migrate_resp resp; ++ struct ucma_event *uevent, *tmp; + struct ucma_context *ctx; ++ LIST_HEAD(event_list); + struct fd f; + struct ucma_file *cur_file; + int ret = 0; +@@ -1641,43 +1611,52 @@ static ssize_t ucma_migrate_id(struct ucma_file *new_file, + ret = -EINVAL; + goto file_put; + } ++ cur_file = f.file->private_data; + + /* Validate current fd and prevent destruction of id. */ +- ctx = ucma_get_ctx(f.file->private_data, cmd.id); ++ ctx = ucma_get_ctx(cur_file, cmd.id); + if (IS_ERR(ctx)) { + ret = PTR_ERR(ctx); + goto file_put; + } + + rdma_lock_handler(ctx->cm_id); +- cur_file = ctx->file; +- if (cur_file == new_file) { +- mutex_lock(&cur_file->mut); +- resp.events_reported = ctx->events_reported; +- mutex_unlock(&cur_file->mut); +- goto response; +- } +- + /* +- * Migrate events between fd's, maintaining order, and avoiding new +- * events being added before existing events. ++ * ctx->file can only be changed under the handler & xa_lock. xa_load() ++ * must be checked again to ensure the ctx hasn't begun destruction ++ * since the ucma_get_ctx(). + */ +- ucma_lock_files(cur_file, new_file); + xa_lock(&ctx_table); +- +- list_move_tail(&ctx->list, &new_file->ctx_list); +- ucma_move_events(ctx, new_file); ++ if (_ucma_find_context(cmd.id, cur_file) != ctx) { ++ xa_unlock(&ctx_table); ++ ret = -ENOENT; ++ goto err_unlock; ++ } + ctx->file = new_file; ++ xa_unlock(&ctx_table); ++ ++ mutex_lock(&cur_file->mut); ++ list_del(&ctx->list); ++ /* ++ * At this point lock_handler() prevents addition of new uevents for ++ * this ctx. ++ */ ++ list_for_each_entry_safe(uevent, tmp, &cur_file->event_list, list) ++ if (uevent->ctx == ctx) ++ list_move_tail(&uevent->list, &event_list); + resp.events_reported = ctx->events_reported; ++ mutex_unlock(&cur_file->mut); + +- xa_unlock(&ctx_table); +- ucma_unlock_files(cur_file, new_file); ++ mutex_lock(&new_file->mut); ++ list_add_tail(&ctx->list, &new_file->ctx_list); ++ list_splice_tail(&event_list, &new_file->event_list); ++ mutex_unlock(&new_file->mut); + +-response: + if (copy_to_user(u64_to_user_ptr(cmd.response), + &resp, sizeof(resp))) + ret = -EFAULT; + ++err_unlock: + rdma_unlock_handler(ctx->cm_id); + ucma_put_ctx(ctx); + file_put: +-- +cgit diff --git a/tests/cases/CVE-2020-36386.patch b/tests/cases/CVE-2020-36386.patch new file mode 100644 index 0000000..53533db --- /dev/null +++ b/tests/cases/CVE-2020-36386.patch @@ -0,0 +1,38 @@ +From 51c19bf3d5cfaa66571e4b88ba2a6f6295311101 Mon Sep 17 00:00:00 2001 +From: Peilin Ye +Date: Fri, 10 Jul 2020 12:09:15 -0400 +Subject: Bluetooth: Fix slab-out-of-bounds read in + hci_extended_inquiry_result_evt() + +Check upon `num_rsp` is insufficient. A malformed event packet with a +large `num_rsp` number makes hci_extended_inquiry_result_evt() go out +of bounds. Fix it. + +This patch fixes the following syzbot bug: + + https://syzkaller.appspot.com/bug?id=4bf11aa05c4ca51ce0df86e500fce486552dc8d2 + +Reported-by: syzbot+d8489a79b781849b9c46@syzkaller.appspotmail.com +Cc: stable@vger.kernel.org +Signed-off-by: Peilin Ye +Acked-by: Greg Kroah-Hartman +Signed-off-by: Marcel Holtmann +--- + net/bluetooth/hci_event.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c +index 03a0759f2fc25..13d8802b81373 100644 +--- a/net/bluetooth/hci_event.c ++++ b/net/bluetooth/hci_event.c +@@ -4375,7 +4375,7 @@ static void hci_extended_inquiry_result_evt(struct hci_dev *hdev, + + BT_DBG("%s num_rsp %d", hdev->name, num_rsp); + +- if (!num_rsp) ++ if (!num_rsp || skb->len < num_rsp * sizeof(*info) + 1) + return; + + if (hci_dev_test_flag(hdev, HCI_PERIODIC_INQ)) +-- +cgit diff --git a/tests/cases/CVE-2020-36387.patch b/tests/cases/CVE-2020-36387.patch new file mode 100644 index 0000000..6857203 --- /dev/null +++ b/tests/cases/CVE-2020-36387.patch @@ -0,0 +1,109 @@ +From 6d816e088c359866f9867057e04f244c608c42fe Mon Sep 17 00:00:00 2001 +From: Jens Axboe +Date: Tue, 11 Aug 2020 08:04:14 -0600 +Subject: io_uring: hold 'ctx' reference around task_work queue + execute + +We're holding the request reference, but we need to go one higher +to ensure that the ctx remains valid after the request has finished. +If the ring is closed with pending task_work inflight, and the +given io_kiocb finishes sync during issue, then we need a reference +to the ring itself around the task_work execution cycle. + +Cc: stable@vger.kernel.org # v5.7+ +Reported-by: syzbot+9b260fc33297966f5a8e@syzkaller.appspotmail.com +Signed-off-by: Jens Axboe +--- + fs/io_uring.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/fs/io_uring.c b/fs/io_uring.c +index 5488698189da8..99582cf5106b4 100644 +--- a/fs/io_uring.c ++++ b/fs/io_uring.c +@@ -1821,8 +1821,10 @@ static void __io_req_task_submit(struct io_kiocb *req) + static void io_req_task_submit(struct callback_head *cb) + { + struct io_kiocb *req = container_of(cb, struct io_kiocb, task_work); ++ struct io_ring_ctx *ctx = req->ctx; + + __io_req_task_submit(req); ++ percpu_ref_put(&ctx->refs); + } + + static void io_req_task_queue(struct io_kiocb *req) +@@ -1830,6 +1832,7 @@ static void io_req_task_queue(struct io_kiocb *req) + int ret; + + init_task_work(&req->task_work, io_req_task_submit); ++ percpu_ref_get(&req->ctx->refs); + + ret = io_req_task_work_add(req, &req->task_work); + if (unlikely(ret)) { +@@ -2318,6 +2321,8 @@ static void io_rw_resubmit(struct callback_head *cb) + refcount_inc(&req->refs); + io_queue_async_work(req); + } ++ ++ percpu_ref_put(&ctx->refs); + } + #endif + +@@ -2330,6 +2335,8 @@ static bool io_rw_reissue(struct io_kiocb *req, long res) + return false; + + init_task_work(&req->task_work, io_rw_resubmit); ++ percpu_ref_get(&req->ctx->refs); ++ + ret = io_req_task_work_add(req, &req->task_work); + if (!ret) + return true; +@@ -3033,6 +3040,8 @@ static int io_async_buf_func(struct wait_queue_entry *wait, unsigned mode, + list_del_init(&wait->entry); + + init_task_work(&req->task_work, io_req_task_submit); ++ percpu_ref_get(&req->ctx->refs); ++ + /* submit ref gets dropped, acquire a new one */ + refcount_inc(&req->refs); + ret = io_req_task_work_add(req, &req->task_work); +@@ -4565,6 +4574,8 @@ static int __io_async_wake(struct io_kiocb *req, struct io_poll_iocb *poll, + + req->result = mask; + init_task_work(&req->task_work, func); ++ percpu_ref_get(&req->ctx->refs); ++ + /* + * If this fails, then the task is exiting. When a task exits, the + * work gets canceled, so just cancel this request as well instead +@@ -4652,11 +4663,13 @@ static void io_poll_task_handler(struct io_kiocb *req, struct io_kiocb **nxt) + static void io_poll_task_func(struct callback_head *cb) + { + struct io_kiocb *req = container_of(cb, struct io_kiocb, task_work); ++ struct io_ring_ctx *ctx = req->ctx; + struct io_kiocb *nxt = NULL; + + io_poll_task_handler(req, &nxt); + if (nxt) + __io_req_task_submit(nxt); ++ percpu_ref_put(&ctx->refs); + } + + static int io_poll_double_wake(struct wait_queue_entry *wait, unsigned mode, +@@ -4752,6 +4765,7 @@ static void io_async_task_func(struct callback_head *cb) + + if (io_poll_rewait(req, &apoll->poll)) { + spin_unlock_irq(&ctx->completion_lock); ++ percpu_ref_put(&ctx->refs); + return; + } + +@@ -4767,6 +4781,7 @@ static void io_async_task_func(struct callback_head *cb) + else + __io_req_task_cancel(req, -ECANCELED); + ++ percpu_ref_put(&ctx->refs); + kfree(apoll->double_poll); + kfree(apoll); + } +-- +cgit diff --git a/tests/cases/CVE-2020-8835.patch b/tests/cases/CVE-2020-8835.patch new file mode 100644 index 0000000..08f6747 --- /dev/null +++ b/tests/cases/CVE-2020-8835.patch @@ -0,0 +1,273 @@ +From f2d67fec0b43edce8c416101cdc52e71145b5fef Mon Sep 17 00:00:00 2001 +From: Daniel Borkmann +Date: Mon, 30 Mar 2020 18:03:22 +0200 +Subject: bpf: Undo incorrect __reg_bound_offset32 handling + +Anatoly has been fuzzing with kBdysch harness and reported a hang in +one of the outcomes: + + 0: (b7) r0 = 808464432 + 1: (7f) r0 >>= r0 + 2: (14) w0 -= 808464432 + 3: (07) r0 += 808464432 + 4: (b7) r1 = 808464432 + 5: (de) if w1 s<= w0 goto pc+0 + R0_w=invP(id=0,umin_value=808464432,umax_value=5103431727,var_off=(0x30303020;0x10000001f)) R1_w=invP808464432 R10=fp0 + 6: (07) r0 += -2144337872 + 7: (14) w0 -= -1607454672 + 8: (25) if r0 > 0x30303030 goto pc+0 + R0_w=invP(id=0,umin_value=271581184,umax_value=271581311,var_off=(0x10300000;0x7f)) R1_w=invP808464432 R10=fp0 + 9: (76) if w0 s>= 0x303030 goto pc+2 + 12: (95) exit + + from 8 to 9: safe + + from 5 to 6: R0_w=invP(id=0,umin_value=808464432,umax_value=5103431727,var_off=(0x30303020;0x10000001f)) R1_w=invP808464432 R10=fp0 + 6: (07) r0 += -2144337872 + 7: (14) w0 -= -1607454672 + 8: (25) if r0 > 0x30303030 goto pc+0 + R0_w=invP(id=0,umin_value=271581184,umax_value=271581311,var_off=(0x10300000;0x7f)) R1_w=invP808464432 R10=fp0 + 9: safe + + from 8 to 9: safe + verification time 589 usec + stack depth 0 + processed 17 insns (limit 1000000) [...] + +The underlying program was xlated as follows: + + # bpftool p d x i 9 + 0: (b7) r0 = 808464432 + 1: (7f) r0 >>= r0 + 2: (14) w0 -= 808464432 + 3: (07) r0 += 808464432 + 4: (b7) r1 = 808464432 + 5: (de) if w1 s<= w0 goto pc+0 + 6: (07) r0 += -2144337872 + 7: (14) w0 -= -1607454672 + 8: (25) if r0 > 0x30303030 goto pc+0 + 9: (76) if w0 s>= 0x303030 goto pc+2 + 10: (05) goto pc-1 + 11: (05) goto pc-1 + 12: (95) exit + +The verifier rewrote original instructions it recognized as dead code with +'goto pc-1', but reality differs from verifier simulation in that we're +actually able to trigger a hang due to hitting the 'goto pc-1' instructions. + +Taking different examples to make the issue more obvious: in this example +we're probing bounds on a completely unknown scalar variable in r1: + + [...] + 5: R0_w=inv1 R1_w=inv(id=0) R10=fp0 + 5: (18) r2 = 0x4000000000 + 7: R0_w=inv1 R1_w=inv(id=0) R2_w=inv274877906944 R10=fp0 + 7: (18) r3 = 0x2000000000 + 9: R0_w=inv1 R1_w=inv(id=0) R2_w=inv274877906944 R3_w=inv137438953472 R10=fp0 + 9: (18) r4 = 0x400 + 11: R0_w=inv1 R1_w=inv(id=0) R2_w=inv274877906944 R3_w=inv137438953472 R4_w=inv1024 R10=fp0 + 11: (18) r5 = 0x200 + 13: R0_w=inv1 R1_w=inv(id=0) R2_w=inv274877906944 R3_w=inv137438953472 R4_w=inv1024 R5_w=inv512 R10=fp0 + 13: (2d) if r1 > r2 goto pc+4 + R0_w=inv1 R1_w=inv(id=0,umax_value=274877906944,var_off=(0x0; 0x7fffffffff)) R2_w=inv274877906944 R3_w=inv137438953472 R4_w=inv1024 R5_w=inv512 R10=fp0 + 14: R0_w=inv1 R1_w=inv(id=0,umax_value=274877906944,var_off=(0x0; 0x7fffffffff)) R2_w=inv274877906944 R3_w=inv137438953472 R4_w=inv1024 R5_w=inv512 R10=fp0 + 14: (ad) if r1 < r3 goto pc+3 + R0_w=inv1 R1_w=inv(id=0,umin_value=137438953472,umax_value=274877906944,var_off=(0x0; 0x7fffffffff)) R2_w=inv274877906944 R3_w=inv137438953472 R4_w=inv1024 R5_w=inv512 R10=fp0 + 15: R0=inv1 R1=inv(id=0,umin_value=137438953472,umax_value=274877906944,var_off=(0x0; 0x7fffffffff)) R2=inv274877906944 R3=inv137438953472 R4=inv1024 R5=inv512 R10=fp0 + 15: (2e) if w1 > w4 goto pc+2 + R0=inv1 R1=inv(id=0,umin_value=137438953472,umax_value=274877906944,var_off=(0x0; 0x7f00000000)) R2=inv274877906944 R3=inv137438953472 R4=inv1024 R5=inv512 R10=fp0 + 16: R0=inv1 R1=inv(id=0,umin_value=137438953472,umax_value=274877906944,var_off=(0x0; 0x7f00000000)) R2=inv274877906944 R3=inv137438953472 R4=inv1024 R5=inv512 R10=fp0 + 16: (ae) if w1 < w5 goto pc+1 + R0=inv1 R1=inv(id=0,umin_value=137438953472,umax_value=274877906944,var_off=(0x0; 0x7f00000000)) R2=inv274877906944 R3=inv137438953472 R4=inv1024 R5=inv512 R10=fp0 + [...] + +We're first probing lower/upper bounds via jmp64, later we do a similar +check via jmp32 and examine the resulting var_off there. After fall-through +in insn 14, we get the following bounded r1 with 0x7fffffffff unknown marked +bits in the variable section. + +Thus, after knowing r1 <= 0x4000000000 and r1 >= 0x2000000000: + + max: 0b100000000000000000000000000000000000000 / 0x4000000000 + var: 0b111111111111111111111111111111111111111 / 0x7fffffffff + min: 0b010000000000000000000000000000000000000 / 0x2000000000 + +Now, in insn 15 and 16, we perform a similar probe with lower/upper bounds +in jmp32. + +Thus, after knowing r1 <= 0x4000000000 and r1 >= 0x2000000000 and + w1 <= 0x400 and w1 >= 0x200: + + max: 0b100000000000000000000000000000000000000 / 0x4000000000 + var: 0b111111100000000000000000000000000000000 / 0x7f00000000 + min: 0b010000000000000000000000000000000000000 / 0x2000000000 + +The lower/upper bounds haven't changed since they have high bits set in +u64 space and the jmp32 tests can only refine bounds in the low bits. + +However, for the var part the expectation would have been 0x7f000007ff +or something less precise up to 0x7fffffffff. A outcome of 0x7f00000000 +is not correct since it would contradict the earlier probed bounds +where we know that the result should have been in [0x200,0x400] in u32 +space. Therefore, tests with such info will lead to wrong verifier +assumptions later on like falsely predicting conditional jumps to be +always taken, etc. + +The issue here is that __reg_bound_offset32()'s implementation from +commit 581738a681b6 ("bpf: Provide better register bounds after jmp32 +instructions") makes an incorrect range assumption: + + static void __reg_bound_offset32(struct bpf_reg_state *reg) + { + u64 mask = 0xffffFFFF; + struct tnum range = tnum_range(reg->umin_value & mask, + reg->umax_value & mask); + struct tnum lo32 = tnum_cast(reg->var_off, 4); + struct tnum hi32 = tnum_lshift(tnum_rshift(reg->var_off, 32), 32); + + reg->var_off = tnum_or(hi32, tnum_intersect(lo32, range)); + } + +In the above walk-through example, __reg_bound_offset32() as-is chose +a range after masking with 0xffffffff of [0x0,0x0] since umin:0x2000000000 +and umax:0x4000000000 and therefore the lo32 part was clamped to 0x0 as +well. However, in the umin:0x2000000000 and umax:0x4000000000 range above +we'd end up with an actual possible interval of [0x0,0xffffffff] for u32 +space instead. + +In case of the original reproducer, the situation looked as follows at +insn 5 for r0: + + [...] + 5: R0_w=invP(id=0,umin_value=808464432,umax_value=5103431727,var_off=(0x0; 0x1ffffffff)) R1_w=invP808464432 R10=fp0 + 0x30303030 0x13030302f + 5: (de) if w1 s<= w0 goto pc+0 + R0_w=invP(id=0,umin_value=808464432,umax_value=5103431727,var_off=(0x30303020; 0x10000001f)) R1_w=invP808464432 R10=fp0 + 0x30303030 0x13030302f + [...] + +After the fall-through, we similarly forced the var_off result into +the wrong range [0x30303030,0x3030302f] suggesting later on that fixed +bits must only be of 0x30303020 with 0x10000001f unknowns whereas such +assumption can only be made when both bounds in hi32 range match. + +Originally, I was thinking to fix this by moving reg into a temp reg and +use proper coerce_reg_to_size() helper on the temp reg where we can then +based on that define the range tnum for later intersection: + + static void __reg_bound_offset32(struct bpf_reg_state *reg) + { + struct bpf_reg_state tmp = *reg; + struct tnum lo32, hi32, range; + + coerce_reg_to_size(&tmp, 4); + range = tnum_range(tmp.umin_value, tmp.umax_value); + lo32 = tnum_cast(reg->var_off, 4); + hi32 = tnum_lshift(tnum_rshift(reg->var_off, 32), 32); + reg->var_off = tnum_or(hi32, tnum_intersect(lo32, range)); + } + +In the case of the concrete example, this gives us a more conservative unknown +section. Thus, after knowing r1 <= 0x4000000000 and r1 >= 0x2000000000 and + w1 <= 0x400 and w1 >= 0x200: + + max: 0b100000000000000000000000000000000000000 / 0x4000000000 + var: 0b111111111111111111111111111111111111111 / 0x7fffffffff + min: 0b010000000000000000000000000000000000000 / 0x2000000000 + +However, above new __reg_bound_offset32() has no effect on refining the +knowledge of the register contents. Meaning, if the bounds in hi32 range +mismatch we'll get the identity function given the range reg spans +[0x0,0xffffffff] and we cast var_off into lo32 only to later on binary +or it again with the hi32. + +Likewise, if the bounds in hi32 range match, then we mask both bounds +with 0xffffffff, use the resulting umin/umax for the range to later +intersect the lo32 with it. However, _prior_ called __reg_bound_offset() +did already such intersection on the full reg and we therefore would only +repeat the same operation on the lo32 part twice. + +Given this has no effect and the original commit had false assumptions, +this patch reverts the code entirely which is also more straight forward +for stable trees: apparently 581738a681b6 got auto-selected by Sasha's +ML system and misclassified as a fix, so it got sucked into v5.4 where +it should never have landed. A revert is low-risk also from a user PoV +since it requires a recent kernel and llc to opt-into -mcpu=v3 BPF CPU +to generate jmp32 instructions. A proper bounds refinement would need a +significantly more complex approach which is currently being worked, but +no stable material [0]. Hence revert is best option for stable. After the +revert, the original reported program gets rejected as follows: + + 1: (7f) r0 >>= r0 + 2: (14) w0 -= 808464432 + 3: (07) r0 += 808464432 + 4: (b7) r1 = 808464432 + 5: (de) if w1 s<= w0 goto pc+0 + R0_w=invP(id=0,umin_value=808464432,umax_value=5103431727,var_off=(0x0; 0x1ffffffff)) R1_w=invP808464432 R10=fp0 + 6: (07) r0 += -2144337872 + 7: (14) w0 -= -1607454672 + 8: (25) if r0 > 0x30303030 goto pc+0 + R0_w=invP(id=0,umax_value=808464432,var_off=(0x0; 0x3fffffff)) R1_w=invP808464432 R10=fp0 + 9: (76) if w0 s>= 0x303030 goto pc+2 + R0=invP(id=0,umax_value=3158063,var_off=(0x0; 0x3fffff)) R1=invP808464432 R10=fp0 + 10: (30) r0 = *(u8 *)skb[808464432] + BPF_LD_[ABS|IND] uses reserved fields + processed 11 insns (limit 1000000) [...] + + [0] https://lore.kernel.org/bpf/158507130343.15666.8018068546764556975.stgit@john-Precision-5820-Tower/T/ + +Fixes: 581738a681b6 ("bpf: Provide better register bounds after jmp32 instructions") +Reported-by: Anatoly Trosinenko +Signed-off-by: Daniel Borkmann +Signed-off-by: Alexei Starovoitov +Link: https://lore.kernel.org/bpf/20200330160324.15259-2-daniel@iogearbox.net +--- + kernel/bpf/verifier.c | 19 ------------------- + 1 file changed, 19 deletions(-) + +diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c +index 047b2e8763996..2a84f73a93a11 100644 +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -1036,17 +1036,6 @@ static void __reg_bound_offset(struct bpf_reg_state *reg) + reg->umax_value)); + } + +-static void __reg_bound_offset32(struct bpf_reg_state *reg) +-{ +- u64 mask = 0xffffFFFF; +- struct tnum range = tnum_range(reg->umin_value & mask, +- reg->umax_value & mask); +- struct tnum lo32 = tnum_cast(reg->var_off, 4); +- struct tnum hi32 = tnum_lshift(tnum_rshift(reg->var_off, 32), 32); +- +- reg->var_off = tnum_or(hi32, tnum_intersect(lo32, range)); +-} +- + /* Reset the min/max bounds of a register */ + static void __mark_reg_unbounded(struct bpf_reg_state *reg) + { +@@ -5805,10 +5794,6 @@ static void reg_set_min_max(struct bpf_reg_state *true_reg, + /* We might have learned some bits from the bounds. */ + __reg_bound_offset(false_reg); + __reg_bound_offset(true_reg); +- if (is_jmp32) { +- __reg_bound_offset32(false_reg); +- __reg_bound_offset32(true_reg); +- } + /* Intersecting with the old var_off might have improved our bounds + * slightly. e.g. if umax was 0x7f...f and var_off was (0; 0xf...fc), + * then new var_off is (0; 0x7f...fc) which improves our umax. +@@ -5918,10 +5903,6 @@ static void reg_set_min_max_inv(struct bpf_reg_state *true_reg, + /* We might have learned some bits from the bounds. */ + __reg_bound_offset(false_reg); + __reg_bound_offset(true_reg); +- if (is_jmp32) { +- __reg_bound_offset32(false_reg); +- __reg_bound_offset32(true_reg); +- } + /* Intersecting with the old var_off might have improved our bounds + * slightly. e.g. if umax was 0x7f...f and var_off was (0; 0xf...fc), + * then new var_off is (0; 0x7f...fc) which improves our umax. +-- +cgit diff --git a/tests/cases/CVE-2021-22543.patch b/tests/cases/CVE-2021-22543.patch new file mode 100644 index 0000000..dfd99ec --- /dev/null +++ b/tests/cases/CVE-2021-22543.patch @@ -0,0 +1,69 @@ +From f8be156be163a052a067306417cd0ff679068c97 Mon Sep 17 00:00:00 2001 +From: Nicholas Piggin +Date: Thu, 24 Jun 2021 08:29:04 -0400 +Subject: KVM: do not allow mapping valid but non-reference-counted pages + +It's possible to create a region which maps valid but non-refcounted +pages (e.g., tail pages of non-compound higher order allocations). These +host pages can then be returned by gfn_to_page, gfn_to_pfn, etc., family +of APIs, which take a reference to the page, which takes it from 0 to 1. +When the reference is dropped, this will free the page incorrectly. + +Fix this by only taking a reference on valid pages if it was non-zero, +which indicates it is participating in normal refcounting (and can be +released with put_page). + +This addresses CVE-2021-22543. + +Signed-off-by: Nicholas Piggin +Tested-by: Paolo Bonzini +Cc: stable@vger.kernel.org +Signed-off-by: Paolo Bonzini +--- + virt/kvm/kvm_main.c | 19 +++++++++++++++++-- + 1 file changed, 17 insertions(+), 2 deletions(-) + +diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c +index 6a6bc7af0e28d..46fb042837d20 100644 +--- a/virt/kvm/kvm_main.c ++++ b/virt/kvm/kvm_main.c +@@ -2055,6 +2055,13 @@ static bool vma_is_valid(struct vm_area_struct *vma, bool write_fault) + return true; + } + ++static int kvm_try_get_pfn(kvm_pfn_t pfn) ++{ ++ if (kvm_is_reserved_pfn(pfn)) ++ return 1; ++ return get_page_unless_zero(pfn_to_page(pfn)); ++} ++ + static int hva_to_pfn_remapped(struct vm_area_struct *vma, + unsigned long addr, bool *async, + bool write_fault, bool *writable, +@@ -2104,13 +2111,21 @@ static int hva_to_pfn_remapped(struct vm_area_struct *vma, + * Whoever called remap_pfn_range is also going to call e.g. + * unmap_mapping_range before the underlying pages are freed, + * causing a call to our MMU notifier. ++ * ++ * Certain IO or PFNMAP mappings can be backed with valid ++ * struct pages, but be allocated without refcounting e.g., ++ * tail pages of non-compound higher order allocations, which ++ * would then underflow the refcount when the caller does the ++ * required put_page. Don't allow those pages here. + */ +- kvm_get_pfn(pfn); ++ if (!kvm_try_get_pfn(pfn)) ++ r = -EFAULT; + + out: + pte_unmap_unlock(ptep, ptl); + *p_pfn = pfn; +- return 0; ++ ++ return r; + } + + /* +-- +cgit diff --git a/tests/cases/CVE-2021-22555.patch b/tests/cases/CVE-2021-22555.patch new file mode 100644 index 0000000..6faee83 --- /dev/null +++ b/tests/cases/CVE-2021-22555.patch @@ -0,0 +1,65 @@ +From b29c457a6511435960115c0f548c4360d5f4801d Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Wed, 7 Apr 2021 21:38:57 +0200 +Subject: netfilter: x_tables: fix compat match/target pad out-of-bound write + +xt_compat_match/target_from_user doesn't check that zeroing the area +to start of next rule won't write past end of allocated ruleset blob. + +Remove this code and zero the entire blob beforehand. + +Reported-by: syzbot+cfc0247ac173f597aaaa@syzkaller.appspotmail.com +Reported-by: Andy Nguyen +Fixes: 9fa492cdc160c ("[NETFILTER]: x_tables: simplify compat API") +Signed-off-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +--- + net/netfilter/x_tables.c | 10 ++-------- + 1 file changed, 2 insertions(+), 8 deletions(-) + +(limited to 'net/netfilter/x_tables.c') + +diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c +index 6bd31a7a27fc5..92e9d4ebc5e8d 100644 +--- a/net/netfilter/x_tables.c ++++ b/net/netfilter/x_tables.c +@@ -733,7 +733,7 @@ void xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr, + { + const struct xt_match *match = m->u.kernel.match; + struct compat_xt_entry_match *cm = (struct compat_xt_entry_match *)m; +- int pad, off = xt_compat_match_offset(match); ++ int off = xt_compat_match_offset(match); + u_int16_t msize = cm->u.user.match_size; + char name[sizeof(m->u.user.name)]; + +@@ -743,9 +743,6 @@ void xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr, + match->compat_from_user(m->data, cm->data); + else + memcpy(m->data, cm->data, msize - sizeof(*cm)); +- pad = XT_ALIGN(match->matchsize) - match->matchsize; +- if (pad > 0) +- memset(m->data + match->matchsize, 0, pad); + + msize += off; + m->u.user.match_size = msize; +@@ -1116,7 +1113,7 @@ void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr, + { + const struct xt_target *target = t->u.kernel.target; + struct compat_xt_entry_target *ct = (struct compat_xt_entry_target *)t; +- int pad, off = xt_compat_target_offset(target); ++ int off = xt_compat_target_offset(target); + u_int16_t tsize = ct->u.user.target_size; + char name[sizeof(t->u.user.name)]; + +@@ -1126,9 +1123,6 @@ void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr, + target->compat_from_user(t->data, ct->data); + else + memcpy(t->data, ct->data, tsize - sizeof(*ct)); +- pad = XT_ALIGN(target->targetsize) - target->targetsize; +- if (pad > 0) +- memset(t->data + target->targetsize, 0, pad); + + tsize += off; + t->u.user.target_size = tsize; +-- +cgit diff --git a/tests/cases/CVE-2021-22600.patch b/tests/cases/CVE-2021-22600.patch new file mode 100644 index 0000000..dde83c2 --- /dev/null +++ b/tests/cases/CVE-2021-22600.patch @@ -0,0 +1,40 @@ +From ec6af094ea28f0f2dda1a6a33b14cd57e36a9755 Mon Sep 17 00:00:00 2001 +From: Willem de Bruijn +Date: Wed, 15 Dec 2021 09:39:37 -0500 +Subject: net/packet: rx_owner_map depends on pg_vec + +Packet sockets may switch ring versions. Avoid misinterpreting state +between versions, whose fields share a union. rx_owner_map is only +allocated with a packet ring (pg_vec) and both are swapped together. +If pg_vec is NULL, meaning no packet ring was allocated, then neither +was rx_owner_map. And the field may be old state from a tpacket_v3. + +Fixes: 61fad6816fc1 ("net/packet: tpacket_rcv: avoid a producer race condition") +Reported-by: Syzbot +Signed-off-by: Willem de Bruijn +Reviewed-by: Eric Dumazet +Link: https://lore.kernel.org/r/20211215143937.106178-1-willemdebruijn.kernel@gmail.com +Signed-off-by: Jakub Kicinski +--- + net/packet/af_packet.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c +index 46943a18a10d54..76c2dca7f0a594 100644 +--- a/net/packet/af_packet.c ++++ b/net/packet/af_packet.c +@@ -4492,9 +4492,10 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, + } + + out_free_pg_vec: +- bitmap_free(rx_owner_map); +- if (pg_vec) ++ if (pg_vec) { ++ bitmap_free(rx_owner_map); + free_pg_vec(pg_vec, order, req->tp_block_nr); ++ } + out: + return err; + } +-- +cgit diff --git a/tests/cases/CVE-2021-23134.patch b/tests/cases/CVE-2021-23134.patch new file mode 100644 index 0000000..3c097ad --- /dev/null +++ b/tests/cases/CVE-2021-23134.patch @@ -0,0 +1,72 @@ +From c61760e6940dd4039a7f5e84a6afc9cdbf4d82b6 Mon Sep 17 00:00:00 2001 +From: Or Cohen +Date: Tue, 4 May 2021 10:16:46 +0300 +Subject: net/nfc: fix use-after-free llcp_sock_bind/connect + +Commits 8a4cd82d ("nfc: fix refcount leak in llcp_sock_connect()") +and c33b1cc62 ("nfc: fix refcount leak in llcp_sock_bind()") +fixed a refcount leak bug in bind/connect but introduced a +use-after-free if the same local is assigned to 2 different sockets. + +This can be triggered by the following simple program: + int sock1 = socket( AF_NFC, SOCK_STREAM, NFC_SOCKPROTO_LLCP ); + int sock2 = socket( AF_NFC, SOCK_STREAM, NFC_SOCKPROTO_LLCP ); + memset( &addr, 0, sizeof(struct sockaddr_nfc_llcp) ); + addr.sa_family = AF_NFC; + addr.nfc_protocol = NFC_PROTO_NFC_DEP; + bind( sock1, (struct sockaddr*) &addr, sizeof(struct sockaddr_nfc_llcp) ) + bind( sock2, (struct sockaddr*) &addr, sizeof(struct sockaddr_nfc_llcp) ) + close(sock1); + close(sock2); + +Fix this by assigning NULL to llcp_sock->local after calling +nfc_llcp_local_put. + +This addresses CVE-2021-23134. + +Reported-by: Or Cohen +Reported-by: Nadav Markus +Fixes: c33b1cc62 ("nfc: fix refcount leak in llcp_sock_bind()") +Signed-off-by: Or Cohen +Signed-off-by: David S. Miller +--- + net/nfc/llcp_sock.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c +index a3b46f8888033..53dbe733f9981 100644 +--- a/net/nfc/llcp_sock.c ++++ b/net/nfc/llcp_sock.c +@@ -109,12 +109,14 @@ static int llcp_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) + GFP_KERNEL); + if (!llcp_sock->service_name) { + nfc_llcp_local_put(llcp_sock->local); ++ llcp_sock->local = NULL; + ret = -ENOMEM; + goto put_dev; + } + llcp_sock->ssap = nfc_llcp_get_sdp_ssap(local, llcp_sock); + if (llcp_sock->ssap == LLCP_SAP_MAX) { + nfc_llcp_local_put(llcp_sock->local); ++ llcp_sock->local = NULL; + kfree(llcp_sock->service_name); + llcp_sock->service_name = NULL; + ret = -EADDRINUSE; +@@ -709,6 +711,7 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr, + llcp_sock->ssap = nfc_llcp_get_local_ssap(local); + if (llcp_sock->ssap == LLCP_SAP_MAX) { + nfc_llcp_local_put(llcp_sock->local); ++ llcp_sock->local = NULL; + ret = -ENOMEM; + goto put_dev; + } +@@ -756,6 +759,7 @@ sock_unlink: + sock_llcp_release: + nfc_llcp_put_ssap(local, llcp_sock->ssap); + nfc_llcp_local_put(llcp_sock->local); ++ llcp_sock->local = NULL; + + put_dev: + nfc_put_device(dev); +-- +cgit diff --git a/tests/cases/CVE-2021-26708.patch b/tests/cases/CVE-2021-26708.patch new file mode 100644 index 0000000..c1b55fe --- /dev/null +++ b/tests/cases/CVE-2021-26708.patch @@ -0,0 +1,106 @@ +From c518adafa39f37858697ac9309c6cf1805581446 Mon Sep 17 00:00:00 2001 +From: Alexander Popov +Date: Mon, 1 Feb 2021 11:47:19 +0300 +Subject: vsock: fix the race conditions in multi-transport support + +There are multiple similar bugs implicitly introduced by the +commit c0cfa2d8a788fcf4 ("vsock: add multi-transports support") and +commit 6a2c0962105ae8ce ("vsock: prevent transport modules unloading"). + +The bug pattern: + [1] vsock_sock.transport pointer is copied to a local variable, + [2] lock_sock() is called, + [3] the local variable is used. +VSOCK multi-transport support introduced the race condition: +vsock_sock.transport value may change between [1] and [2]. + +Let's copy vsock_sock.transport pointer to local variables after +the lock_sock() call. + +Fixes: c0cfa2d8a788fcf4 ("vsock: add multi-transports support") +Signed-off-by: Alexander Popov +Reviewed-by: Stefano Garzarella +Reviewed-by: Jorgen Hansen +Link: https://lore.kernel.org/r/20210201084719.2257066-1-alex.popov@linux.com +Signed-off-by: Jakub Kicinski +--- + net/vmw_vsock/af_vsock.c | 17 ++++++++++++----- + 1 file changed, 12 insertions(+), 5 deletions(-) + +diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c +index b12d3a3222428..6894f21dc1475 100644 +--- a/net/vmw_vsock/af_vsock.c ++++ b/net/vmw_vsock/af_vsock.c +@@ -1014,9 +1014,12 @@ static __poll_t vsock_poll(struct file *file, struct socket *sock, + mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND; + + } else if (sock->type == SOCK_STREAM) { +- const struct vsock_transport *transport = vsk->transport; ++ const struct vsock_transport *transport; ++ + lock_sock(sk); + ++ transport = vsk->transport; ++ + /* Listening sockets that have connections in their accept + * queue can be read. + */ +@@ -1099,10 +1102,11 @@ static int vsock_dgram_sendmsg(struct socket *sock, struct msghdr *msg, + err = 0; + sk = sock->sk; + vsk = vsock_sk(sk); +- transport = vsk->transport; + + lock_sock(sk); + ++ transport = vsk->transport; ++ + err = vsock_auto_bind(vsk); + if (err) + goto out; +@@ -1561,10 +1565,11 @@ static int vsock_stream_setsockopt(struct socket *sock, + err = 0; + sk = sock->sk; + vsk = vsock_sk(sk); +- transport = vsk->transport; + + lock_sock(sk); + ++ transport = vsk->transport; ++ + switch (optname) { + case SO_VM_SOCKETS_BUFFER_SIZE: + COPY_IN(val); +@@ -1697,7 +1702,6 @@ static int vsock_stream_sendmsg(struct socket *sock, struct msghdr *msg, + + sk = sock->sk; + vsk = vsock_sk(sk); +- transport = vsk->transport; + total_written = 0; + err = 0; + +@@ -1706,6 +1710,8 @@ static int vsock_stream_sendmsg(struct socket *sock, struct msghdr *msg, + + lock_sock(sk); + ++ transport = vsk->transport; ++ + /* Callers should not provide a destination with stream sockets. */ + if (msg->msg_namelen) { + err = sk->sk_state == TCP_ESTABLISHED ? -EISCONN : -EOPNOTSUPP; +@@ -1840,11 +1846,12 @@ vsock_stream_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, + + sk = sock->sk; + vsk = vsock_sk(sk); +- transport = vsk->transport; + err = 0; + + lock_sock(sk); + ++ transport = vsk->transport; ++ + if (!transport || sk->sk_state != TCP_ESTABLISHED) { + /* Recvmsg is supposed to return 0 if a peer performs an + * orderly shutdown. Differentiate between that case and when a +-- +cgit diff --git a/tests/cases/CVE-2021-27364.patch b/tests/cases/CVE-2021-27364.patch new file mode 100644 index 0000000..b329c7c --- /dev/null +++ b/tests/cases/CVE-2021-27364.patch @@ -0,0 +1,46 @@ +From 688e8128b7a92df982709a4137ea4588d16f24aa Mon Sep 17 00:00:00 2001 +From: Lee Duncan +Date: Tue, 23 Feb 2021 13:06:24 -0800 +Subject: scsi: iscsi: Restrict sessions and handles to admin capabilities + +Protect the iSCSI transport handle, available in sysfs, by requiring +CAP_SYS_ADMIN to read it. Also protect the netlink socket by restricting +reception of messages to ones sent with CAP_SYS_ADMIN. This disables +normal users from being able to end arbitrary iSCSI sessions. + +Cc: stable@vger.kernel.org +Reported-by: Adam Nichols +Reviewed-by: Chris Leech +Reviewed-by: Mike Christie +Signed-off-by: Lee Duncan +Signed-off-by: Martin K. Petersen +--- + drivers/scsi/scsi_transport_iscsi.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c +index 969d24d580e29..69a1f55499ef9 100644 +--- a/drivers/scsi/scsi_transport_iscsi.c ++++ b/drivers/scsi/scsi_transport_iscsi.c +@@ -132,6 +132,9 @@ show_transport_handle(struct device *dev, struct device_attribute *attr, + char *buf) + { + struct iscsi_internal *priv = dev_to_iscsi_internal(dev); ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EACCES; + return sprintf(buf, "%llu\n", (unsigned long long)iscsi_handle(priv->iscsi_transport)); + } + static DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL); +@@ -3621,6 +3624,9 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) + struct iscsi_cls_conn *conn; + struct iscsi_endpoint *ep = NULL; + ++ if (!netlink_capable(skb, CAP_SYS_ADMIN)) ++ return -EPERM; ++ + if (nlh->nlmsg_type == ISCSI_UEVENT_PATH_UPDATE) + *group = ISCSI_NL_GRP_UIP; + else +-- +cgit diff --git a/tests/cases/CVE-2021-27365.patch b/tests/cases/CVE-2021-27365.patch new file mode 100644 index 0000000..27e2878 --- /dev/null +++ b/tests/cases/CVE-2021-27365.patch @@ -0,0 +1,48 @@ +From f9dbdf97a5bd92b1a49cee3d591b55b11fd7a6d5 Mon Sep 17 00:00:00 2001 +From: Chris Leech +Date: Tue, 23 Feb 2021 21:39:01 -0800 +Subject: scsi: iscsi: Verify lengths on passthrough PDUs + +Open-iSCSI sends passthrough PDUs over netlink, but the kernel should be +verifying that the provided PDU header and data lengths fall within the +netlink message to prevent accessing beyond that in memory. + +Cc: stable@vger.kernel.org +Reported-by: Adam Nichols +Reviewed-by: Lee Duncan +Reviewed-by: Mike Christie +Signed-off-by: Chris Leech +Signed-off-by: Martin K. Petersen +--- + drivers/scsi/scsi_transport_iscsi.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c +index c1eff85a8976c..91074fd97f644 100644 +--- a/drivers/scsi/scsi_transport_iscsi.c ++++ b/drivers/scsi/scsi_transport_iscsi.c +@@ -3624,6 +3624,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) + { + int err = 0; + u32 portid; ++ u32 pdu_len; + struct iscsi_uevent *ev = nlmsg_data(nlh); + struct iscsi_transport *transport = NULL; + struct iscsi_internal *priv; +@@ -3766,6 +3767,14 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) + err = -EINVAL; + break; + case ISCSI_UEVENT_SEND_PDU: ++ pdu_len = nlh->nlmsg_len - sizeof(*nlh) - sizeof(*ev); ++ ++ if ((ev->u.send_pdu.hdr_size > pdu_len) || ++ (ev->u.send_pdu.data_size > (pdu_len - ev->u.send_pdu.hdr_size))) { ++ err = -EINVAL; ++ break; ++ } ++ + conn = iscsi_conn_lookup(ev->u.send_pdu.sid, ev->u.send_pdu.cid); + if (conn) { + mutex_lock(&conn_mutex); +-- +cgit diff --git a/tests/cases/CVE-2021-29657.patch b/tests/cases/CVE-2021-29657.patch new file mode 100644 index 0000000..a30810c --- /dev/null +++ b/tests/cases/CVE-2021-29657.patch @@ -0,0 +1,65 @@ +From a58d9166a756a0f4a6618e4f593232593d6df134 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Wed, 31 Mar 2021 06:24:43 -0400 +Subject: KVM: SVM: load control fields from VMCB12 before checking them + +Avoid races between check and use of the nested VMCB controls. This +for example ensures that the VMRUN intercept is always reflected to the +nested hypervisor, instead of being processed by the host. Without this +patch, it is possible to end up with svm->nested.hsave pointing to +the MSR permission bitmap for nested guests. + +This bug is CVE-2021-29657. + +Reported-by: Felix Wilhelm +Cc: stable@vger.kernel.org +Fixes: 2fcf4876ada ("KVM: nSVM: implement on demand allocation of the nested state") +Signed-off-by: Paolo Bonzini +--- + arch/x86/kvm/svm/nested.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c +index 9e4c226dbf7d9..061a00f91af5f 100644 +--- a/arch/x86/kvm/svm/nested.c ++++ b/arch/x86/kvm/svm/nested.c +@@ -225,7 +225,7 @@ static bool nested_vmcb_check_controls(struct vmcb_control_area *control) + return true; + } + +-static bool nested_vmcb_checks(struct vcpu_svm *svm, struct vmcb *vmcb12) ++static bool nested_vmcb_check_save(struct vcpu_svm *svm, struct vmcb *vmcb12) + { + bool vmcb12_lma; + +@@ -257,7 +257,7 @@ static bool nested_vmcb_checks(struct vcpu_svm *svm, struct vmcb *vmcb12) + if (kvm_valid_cr4(&svm->vcpu, vmcb12->save.cr4)) + return false; + +- return nested_vmcb_check_controls(&vmcb12->control); ++ return true; + } + + static void load_nested_vmcb_control(struct vcpu_svm *svm, +@@ -440,7 +440,6 @@ int enter_svm_guest_mode(struct vcpu_svm *svm, u64 vmcb12_gpa, + int ret; + + svm->nested.vmcb12_gpa = vmcb12_gpa; +- load_nested_vmcb_control(svm, &vmcb12->control); + nested_prepare_vmcb_save(svm, vmcb12); + nested_prepare_vmcb_control(svm); + +@@ -484,7 +483,10 @@ int nested_svm_vmrun(struct vcpu_svm *svm) + if (WARN_ON_ONCE(!svm->nested.initialized)) + return -EINVAL; + +- if (!nested_vmcb_checks(svm, vmcb12)) { ++ load_nested_vmcb_control(svm, &vmcb12->control); ++ ++ if (!nested_vmcb_check_save(svm, vmcb12) || ++ !nested_vmcb_check_controls(&svm->nested.ctl)) { + vmcb12->control.exit_code = SVM_EXIT_ERR; + vmcb12->control.exit_code_hi = 0; + vmcb12->control.exit_info_1 = 0; +-- +cgit diff --git a/tests/cases/CVE-2021-33033.patch b/tests/cases/CVE-2021-33033.patch new file mode 100644 index 0000000..459cf7d --- /dev/null +++ b/tests/cases/CVE-2021-33033.patch @@ -0,0 +1,137 @@ +From ad5d07f4a9cd671233ae20983848874731102c08 Mon Sep 17 00:00:00 2001 +From: Paul Moore +Date: Thu, 4 Mar 2021 16:29:51 -0500 +Subject: cipso,calipso: resolve a number of problems with the DOI refcounts + +The current CIPSO and CALIPSO refcounting scheme for the DOI +definitions is a bit flawed in that we: + +1. Don't correctly match gets/puts in netlbl_cipsov4_list(). +2. Decrement the refcount on each attempt to remove the DOI from the + DOI list, only removing it from the list once the refcount drops + to zero. + +This patch fixes these problems by adding the missing "puts" to +netlbl_cipsov4_list() and introduces a more conventional, i.e. +not-buggy, refcounting mechanism to the DOI definitions. Upon the +addition of a DOI to the DOI list, it is initialized with a refcount +of one, removing a DOI from the list removes it from the list and +drops the refcount by one; "gets" and "puts" behave as expected with +respect to refcounts, increasing and decreasing the DOI's refcount by +one. + +Fixes: b1edeb102397 ("netlabel: Replace protocol/NetLabel linking with refrerence counts") +Fixes: d7cce01504a0 ("netlabel: Add support for removing a CALIPSO DOI.") +Reported-by: syzbot+9ec037722d2603a9f52e@syzkaller.appspotmail.com +Signed-off-by: Paul Moore +Signed-off-by: David S. Miller +--- + net/ipv4/cipso_ipv4.c | 11 +---------- + net/ipv6/calipso.c | 14 +++++--------- + net/netlabel/netlabel_cipso_v4.c | 3 +++ + 3 files changed, 9 insertions(+), 19 deletions(-) + +diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c +index 471d33a0d095f..be09c7669a799 100644 +--- a/net/ipv4/cipso_ipv4.c ++++ b/net/ipv4/cipso_ipv4.c +@@ -519,16 +519,10 @@ int cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info) + ret_val = -ENOENT; + goto doi_remove_return; + } +- if (!refcount_dec_and_test(&doi_def->refcount)) { +- spin_unlock(&cipso_v4_doi_list_lock); +- ret_val = -EBUSY; +- goto doi_remove_return; +- } + list_del_rcu(&doi_def->list); + spin_unlock(&cipso_v4_doi_list_lock); + +- cipso_v4_cache_invalidate(); +- call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu); ++ cipso_v4_doi_putdef(doi_def); + ret_val = 0; + + doi_remove_return: +@@ -585,9 +579,6 @@ void cipso_v4_doi_putdef(struct cipso_v4_doi *doi_def) + + if (!refcount_dec_and_test(&doi_def->refcount)) + return; +- spin_lock(&cipso_v4_doi_list_lock); +- list_del_rcu(&doi_def->list); +- spin_unlock(&cipso_v4_doi_list_lock); + + cipso_v4_cache_invalidate(); + call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu); +diff --git a/net/ipv6/calipso.c b/net/ipv6/calipso.c +index 51184a70ac7e5..1578ed9e97d89 100644 +--- a/net/ipv6/calipso.c ++++ b/net/ipv6/calipso.c +@@ -83,6 +83,9 @@ struct calipso_map_cache_entry { + + static struct calipso_map_cache_bkt *calipso_cache; + ++static void calipso_cache_invalidate(void); ++static void calipso_doi_putdef(struct calipso_doi *doi_def); ++ + /* Label Mapping Cache Functions + */ + +@@ -444,15 +447,10 @@ static int calipso_doi_remove(u32 doi, struct netlbl_audit *audit_info) + ret_val = -ENOENT; + goto doi_remove_return; + } +- if (!refcount_dec_and_test(&doi_def->refcount)) { +- spin_unlock(&calipso_doi_list_lock); +- ret_val = -EBUSY; +- goto doi_remove_return; +- } + list_del_rcu(&doi_def->list); + spin_unlock(&calipso_doi_list_lock); + +- call_rcu(&doi_def->rcu, calipso_doi_free_rcu); ++ calipso_doi_putdef(doi_def); + ret_val = 0; + + doi_remove_return: +@@ -508,10 +506,8 @@ static void calipso_doi_putdef(struct calipso_doi *doi_def) + + if (!refcount_dec_and_test(&doi_def->refcount)) + return; +- spin_lock(&calipso_doi_list_lock); +- list_del_rcu(&doi_def->list); +- spin_unlock(&calipso_doi_list_lock); + ++ calipso_cache_invalidate(); + call_rcu(&doi_def->rcu, calipso_doi_free_rcu); + } + +diff --git a/net/netlabel/netlabel_cipso_v4.c b/net/netlabel/netlabel_cipso_v4.c +index 726dda95934c6..4f50a64315cf0 100644 +--- a/net/netlabel/netlabel_cipso_v4.c ++++ b/net/netlabel/netlabel_cipso_v4.c +@@ -575,6 +575,7 @@ list_start: + + break; + } ++ cipso_v4_doi_putdef(doi_def); + rcu_read_unlock(); + + genlmsg_end(ans_skb, data); +@@ -583,12 +584,14 @@ list_start: + list_retry: + /* XXX - this limit is a guesstimate */ + if (nlsze_mult < 4) { ++ cipso_v4_doi_putdef(doi_def); + rcu_read_unlock(); + kfree_skb(ans_skb); + nlsze_mult *= 2; + goto list_start; + } + list_failure_lock: ++ cipso_v4_doi_putdef(doi_def); + rcu_read_unlock(); + list_failure: + kfree_skb(ans_skb); +-- +cgit diff --git a/tests/cases/CVE-2021-33909.patch b/tests/cases/CVE-2021-33909.patch new file mode 100644 index 0000000..046544c --- /dev/null +++ b/tests/cases/CVE-2021-33909.patch @@ -0,0 +1,34 @@ +From 8cae8cd89f05f6de223d63e6d15e31c8ba9cf53b Mon Sep 17 00:00:00 2001 +From: Eric Sandeen +Date: Tue, 13 Jul 2021 17:49:23 +0200 +Subject: seq_file: disallow extremely large seq buffer allocations + +There is no reasonable need for a buffer larger than this, and it avoids +int overflow pitfalls. + +Fixes: 058504edd026 ("fs/seq_file: fallback to vmalloc allocation") +Suggested-by: Al Viro +Reported-by: Qualys Security Advisory +Signed-off-by: Eric Sandeen +Cc: stable@kernel.org +Signed-off-by: Linus Torvalds +--- + fs/seq_file.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/fs/seq_file.c b/fs/seq_file.c +index b117b212ef288..4a2cda04d3e29 100644 +--- a/fs/seq_file.c ++++ b/fs/seq_file.c +@@ -32,6 +32,9 @@ static void seq_set_overflow(struct seq_file *m) + + static void *seq_buf_alloc(unsigned long size) + { ++ if (unlikely(size > MAX_RW_COUNT)) ++ return NULL; ++ + return kvmalloc(size, GFP_KERNEL_ACCOUNT); + } + +-- +cgit diff --git a/tests/cases/CVE-2021-34866.patch b/tests/cases/CVE-2021-34866.patch new file mode 100644 index 0000000..a4d15f1 --- /dev/null +++ b/tests/cases/CVE-2021-34866.patch @@ -0,0 +1,53 @@ +From 5b029a32cfe4600f5e10e36b41778506b90fd4de Mon Sep 17 00:00:00 2001 +From: Daniel Borkmann +Date: Mon, 23 Aug 2021 21:02:09 +0200 +Subject: bpf: Fix ringbuf helper function compatibility + +Commit 457f44363a88 ("bpf: Implement BPF ring buffer and verifier support +for it") extended check_map_func_compatibility() by enforcing map -> helper +function match, but not helper -> map type match. + +Due to this all of the bpf_ringbuf_*() helper functions could be used with +a wrong map type such as array or hash map, leading to invalid access due +to type confusion. + +Also, both BPF_FUNC_ringbuf_{submit,discard} have ARG_PTR_TO_ALLOC_MEM as +argument and not a BPF map. Therefore, their check_map_func_compatibility() +presence is incorrect since it's only for map type checking. + +Fixes: 457f44363a88 ("bpf: Implement BPF ring buffer and verifier support for it") +Reported-by: Ryota Shiga (Flatt Security) +Signed-off-by: Daniel Borkmann +Acked-by: Alexei Starovoitov +--- + kernel/bpf/verifier.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c +index 381d3d6f24bcb1..49f07e2bf23b9b 100644 +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -5150,8 +5150,6 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env, + case BPF_MAP_TYPE_RINGBUF: + if (func_id != BPF_FUNC_ringbuf_output && + func_id != BPF_FUNC_ringbuf_reserve && +- func_id != BPF_FUNC_ringbuf_submit && +- func_id != BPF_FUNC_ringbuf_discard && + func_id != BPF_FUNC_ringbuf_query) + goto error; + break; +@@ -5260,6 +5258,12 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env, + if (map->map_type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) + goto error; + break; ++ case BPF_FUNC_ringbuf_output: ++ case BPF_FUNC_ringbuf_reserve: ++ case BPF_FUNC_ringbuf_query: ++ if (map->map_type != BPF_MAP_TYPE_RINGBUF) ++ goto error; ++ break; + case BPF_FUNC_get_stackid: + if (map->map_type != BPF_MAP_TYPE_STACK_TRACE) + goto error; +-- +cgit diff --git a/tests/cases/CVE-2021-3490.patch b/tests/cases/CVE-2021-3490.patch new file mode 100644 index 0000000..ecd9c6b --- /dev/null +++ b/tests/cases/CVE-2021-3490.patch @@ -0,0 +1,111 @@ +From 049c4e13714ecbca567b4d5f6d563f05d431c80e Mon Sep 17 00:00:00 2001 +From: Daniel Borkmann +Date: Mon, 10 May 2021 13:10:44 +0000 +Subject: bpf: Fix alu32 const subreg bound tracking on bitwise operations + +Fix a bug in the verifier's scalar32_min_max_*() functions which leads to +incorrect tracking of 32 bit bounds for the simulation of and/or/xor bitops. +When both the src & dst subreg is a known constant, then the assumption is +that scalar_min_max_*() will take care to update bounds correctly. However, +this is not the case, for example, consider a register R2 which has a tnum +of 0xffffffff00000000, meaning, lower 32 bits are known constant and in this +case of value 0x00000001. R2 is then and'ed with a register R3 which is a +64 bit known constant, here, 0x100000002. + +What can be seen in line '10:' is that 32 bit bounds reach an invalid state +where {u,s}32_min_value > {u,s}32_max_value. The reason is scalar32_min_max_*() +delegates 32 bit bounds updates to scalar_min_max_*(), however, that really +only takes place when both the 64 bit src & dst register is a known constant. +Given scalar32_min_max_*() is intended to be designed as closely as possible +to scalar_min_max_*(), update the 32 bit bounds in this situation through +__mark_reg32_known() which will set all {u,s}32_{min,max}_value to the correct +constant, which is 0x00000000 after the fix (given 0x00000001 & 0x00000002 in +32 bit space). This is possible given var32_off already holds the final value +as dst_reg->var_off is updated before calling scalar32_min_max_*(). + +Before fix, invalid tracking of R2: + + [...] + 9: R0_w=inv1337 R1=ctx(id=0,off=0,imm=0) R2_w=inv(id=0,smin_value=-9223372036854775807 (0x8000000000000001),smax_value=9223372032559808513 (0x7fffffff00000001),umin_value=1,umax_value=0xffffffff00000001,var_off=(0x1; 0xffffffff00000000),s32_min_value=1,s32_max_value=1,u32_min_value=1,u32_max_value=1) R3_w=inv4294967298 R10=fp0 + 9: (5f) r2 &= r3 + 10: R0_w=inv1337 R1=ctx(id=0,off=0,imm=0) R2_w=inv(id=0,smin_value=0,smax_value=4294967296 (0x100000000),umin_value=0,umax_value=0x100000000,var_off=(0x0; 0x100000000),s32_min_value=1,s32_max_value=0,u32_min_value=1,u32_max_value=0) R3_w=inv4294967298 R10=fp0 + [...] + +After fix, correct tracking of R2: + + [...] + 9: R0_w=inv1337 R1=ctx(id=0,off=0,imm=0) R2_w=inv(id=0,smin_value=-9223372036854775807 (0x8000000000000001),smax_value=9223372032559808513 (0x7fffffff00000001),umin_value=1,umax_value=0xffffffff00000001,var_off=(0x1; 0xffffffff00000000),s32_min_value=1,s32_max_value=1,u32_min_value=1,u32_max_value=1) R3_w=inv4294967298 R10=fp0 + 9: (5f) r2 &= r3 + 10: R0_w=inv1337 R1=ctx(id=0,off=0,imm=0) R2_w=inv(id=0,smin_value=0,smax_value=4294967296 (0x100000000),umin_value=0,umax_value=0x100000000,var_off=(0x0; 0x100000000),s32_min_value=0,s32_max_value=0,u32_min_value=0,u32_max_value=0) R3_w=inv4294967298 R10=fp0 + [...] + +Fixes: 3f50f132d840 ("bpf: Verifier, do explicit ALU32 bounds tracking") +Fixes: 2921c90d4718 ("bpf: Fix a verifier failure with xor") +Reported-by: Manfred Paul (@_manfp) +Reported-by: Thadeu Lima de Souza Cascardo +Signed-off-by: Daniel Borkmann +Reviewed-by: John Fastabend +Acked-by: Alexei Starovoitov +--- + kernel/bpf/verifier.c | 22 +++++++++------------- + 1 file changed, 9 insertions(+), 13 deletions(-) + +diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c +index 757476c91c984..9352a1b7de2dd 100644 +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -7084,11 +7084,10 @@ static void scalar32_min_max_and(struct bpf_reg_state *dst_reg, + s32 smin_val = src_reg->s32_min_value; + u32 umax_val = src_reg->u32_max_value; + +- /* Assuming scalar64_min_max_and will be called so its safe +- * to skip updating register for known 32-bit case. +- */ +- if (src_known && dst_known) ++ if (src_known && dst_known) { ++ __mark_reg32_known(dst_reg, var32_off.value); + return; ++ } + + /* We get our minimum from the var_off, since that's inherently + * bitwise. Our maximum is the minimum of the operands' maxima. +@@ -7108,7 +7107,6 @@ static void scalar32_min_max_and(struct bpf_reg_state *dst_reg, + dst_reg->s32_min_value = dst_reg->u32_min_value; + dst_reg->s32_max_value = dst_reg->u32_max_value; + } +- + } + + static void scalar_min_max_and(struct bpf_reg_state *dst_reg, +@@ -7155,11 +7153,10 @@ static void scalar32_min_max_or(struct bpf_reg_state *dst_reg, + s32 smin_val = src_reg->s32_min_value; + u32 umin_val = src_reg->u32_min_value; + +- /* Assuming scalar64_min_max_or will be called so it is safe +- * to skip updating register for known case. +- */ +- if (src_known && dst_known) ++ if (src_known && dst_known) { ++ __mark_reg32_known(dst_reg, var32_off.value); + return; ++ } + + /* We get our maximum from the var_off, and our minimum is the + * maximum of the operands' minima +@@ -7224,11 +7221,10 @@ static void scalar32_min_max_xor(struct bpf_reg_state *dst_reg, + struct tnum var32_off = tnum_subreg(dst_reg->var_off); + s32 smin_val = src_reg->s32_min_value; + +- /* Assuming scalar64_min_max_xor will be called so it is safe +- * to skip updating register for known case. +- */ +- if (src_known && dst_known) ++ if (src_known && dst_known) { ++ __mark_reg32_known(dst_reg, var32_off.value); + return; ++ } + + /* We get both minimum and maximum from the var32_off. */ + dst_reg->u32_min_value = var32_off.value; +-- +cgit diff --git a/tests/cases/CVE-2021-3656.patch b/tests/cases/CVE-2021-3656.patch new file mode 100644 index 0000000..71389e1 --- /dev/null +++ b/tests/cases/CVE-2021-3656.patch @@ -0,0 +1,38 @@ +From c7dfa4009965a9b2d7b329ee970eb8da0d32f0bc Mon Sep 17 00:00:00 2001 +From: Maxim Levitsky +Date: Mon, 19 Jul 2021 16:05:00 +0300 +Subject: KVM: nSVM: always intercept VMLOAD/VMSAVE when nested (CVE-2021-3656) + +If L1 disables VMLOAD/VMSAVE intercepts, and doesn't enable +Virtual VMLOAD/VMSAVE (currently not supported for the nested hypervisor), +then VMLOAD/VMSAVE must operate on the L1 physical memory, which is only +possible by making L0 intercept these instructions. + +Failure to do so allowed the nested guest to run VMLOAD/VMSAVE unintercepted, +and thus read/write portions of the host physical memory. + +Fixes: 89c8a4984fc9 ("KVM: SVM: Enable Virtual VMLOAD VMSAVE feature") + +Suggested-by: Paolo Bonzini +Signed-off-by: Maxim Levitsky +Signed-off-by: Paolo Bonzini +--- + arch/x86/kvm/svm/nested.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c +index 28381ca5221c0..e5515477c30a6 100644 +--- a/arch/x86/kvm/svm/nested.c ++++ b/arch/x86/kvm/svm/nested.c +@@ -158,6 +158,9 @@ void recalc_intercepts(struct vcpu_svm *svm) + /* If SMI is not intercepted, ignore guest SMI intercept as well */ + if (!intercept_smi) + vmcb_clr_intercept(c, INTERCEPT_SMI); ++ ++ vmcb_set_intercept(c, INTERCEPT_VMLOAD); ++ vmcb_set_intercept(c, INTERCEPT_VMSAVE); + } + + static void copy_vmcb_control_area(struct vmcb_control_area *dst, +-- +cgit diff --git a/tests/cases/CVE-2021-3715.patch b/tests/cases/CVE-2021-3715.patch new file mode 100644 index 0000000..20a751f --- /dev/null +++ b/tests/cases/CVE-2021-3715.patch @@ -0,0 +1,44 @@ +From ef299cc3fa1a9e1288665a9fdc8bff55629fd359 Mon Sep 17 00:00:00 2001 +From: Cong Wang +Date: Fri, 13 Mar 2020 22:29:54 -0700 +Subject: net_sched: cls_route: remove the right filter from hashtable + +route4_change() allocates a new filter and copies values from +the old one. After the new filter is inserted into the hash +table, the old filter should be removed and freed, as the final +step of the update. + +However, the current code mistakenly removes the new one. This +looks apparently wrong to me, and it causes double "free" and +use-after-free too, as reported by syzbot. + +Reported-and-tested-by: syzbot+f9b32aaacd60305d9687@syzkaller.appspotmail.com +Reported-and-tested-by: syzbot+2f8c233f131943d6056d@syzkaller.appspotmail.com +Reported-and-tested-by: syzbot+9c2df9fd5e9445b74e01@syzkaller.appspotmail.com +Fixes: 1109c00547fc ("net: sched: RCU cls_route") +Cc: Jamal Hadi Salim +Cc: Jiri Pirko +Cc: John Fastabend +Signed-off-by: Cong Wang +Signed-off-by: David S. Miller +--- + net/sched/cls_route.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c +index 6f8786b06bde1..5efa3e7ace152 100644 +--- a/net/sched/cls_route.c ++++ b/net/sched/cls_route.c +@@ -534,8 +534,8 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, + fp = &b->ht[h]; + for (pfp = rtnl_dereference(*fp); pfp; + fp = &pfp->next, pfp = rtnl_dereference(*fp)) { +- if (pfp == f) { +- *fp = f->next; ++ if (pfp == fold) { ++ rcu_assign_pointer(*fp, fold->next); + break; + } + } +-- +cgit diff --git a/tests/cases/CVE-2021-37576.patch b/tests/cases/CVE-2021-37576.patch new file mode 100644 index 0000000..dddad59 --- /dev/null +++ b/tests/cases/CVE-2021-37576.patch @@ -0,0 +1,74 @@ +From f62f3c20647ebd5fb6ecb8f0b477b9281c44c10a Mon Sep 17 00:00:00 2001 +From: Nicholas Piggin +Date: Tue, 20 Jul 2021 20:43:09 +1000 +Subject: KVM: PPC: Book3S: Fix H_RTAS rets buffer overflow + +The kvmppc_rtas_hcall() sets the host rtas_args.rets pointer based on +the rtas_args.nargs that was provided by the guest. That guest nargs +value is not range checked, so the guest can cause the host rets pointer +to be pointed outside the args array. The individual rtas function +handlers check the nargs and nrets values to ensure they are correct, +but if they are not, the handlers store a -3 (0xfffffffd) failure +indication in rets[0] which corrupts host memory. + +Fix this by testing up front whether the guest supplied nargs and nret +would exceed the array size, and fail the hcall directly without storing +a failure indication to rets[0]. + +Also expand on a comment about why we kill the guest and try not to +return errors directly if we have a valid rets[0] pointer. + +Fixes: 8e591cb72047 ("KVM: PPC: Book3S: Add infrastructure to implement kernel-side RTAS calls") +Cc: stable@vger.kernel.org # v3.10+ +Reported-by: Alexey Kardashevskiy +Signed-off-by: Nicholas Piggin +Signed-off-by: Michael Ellerman +--- + arch/powerpc/kvm/book3s_rtas.c | 25 ++++++++++++++++++++++--- + 1 file changed, 22 insertions(+), 3 deletions(-) + +diff --git a/arch/powerpc/kvm/book3s_rtas.c b/arch/powerpc/kvm/book3s_rtas.c +index c5e677508d3b2..0f847f1e5ddd0 100644 +--- a/arch/powerpc/kvm/book3s_rtas.c ++++ b/arch/powerpc/kvm/book3s_rtas.c +@@ -242,6 +242,17 @@ int kvmppc_rtas_hcall(struct kvm_vcpu *vcpu) + * value so we can restore it on the way out. + */ + orig_rets = args.rets; ++ if (be32_to_cpu(args.nargs) >= ARRAY_SIZE(args.args)) { ++ /* ++ * Don't overflow our args array: ensure there is room for ++ * at least rets[0] (even if the call specifies 0 nret). ++ * ++ * Each handler must then check for the correct nargs and nret ++ * values, but they may always return failure in rets[0]. ++ */ ++ rc = -EINVAL; ++ goto fail; ++ } + args.rets = &args.args[be32_to_cpu(args.nargs)]; + + mutex_lock(&vcpu->kvm->arch.rtas_token_lock); +@@ -269,9 +280,17 @@ int kvmppc_rtas_hcall(struct kvm_vcpu *vcpu) + fail: + /* + * We only get here if the guest has called RTAS with a bogus +- * args pointer. That means we can't get to the args, and so we +- * can't fail the RTAS call. So fail right out to userspace, +- * which should kill the guest. ++ * args pointer or nargs/nret values that would overflow the ++ * array. That means we can't get to the args, and so we can't ++ * fail the RTAS call. So fail right out to userspace, which ++ * should kill the guest. ++ * ++ * SLOF should actually pass the hcall return value from the ++ * rtas handler call in r3, so enter_rtas could be modified to ++ * return a failure indication in r3 and we could return such ++ * errors to the guest rather than failing to host userspace. ++ * However old guests that don't test for failure could then ++ * continue silently after errors, so for now we won't do this. + */ + return rc; + } +-- +cgit diff --git a/tests/cases/CVE-2021-3760.patch b/tests/cases/CVE-2021-3760.patch new file mode 100644 index 0000000..e8560b5 --- /dev/null +++ b/tests/cases/CVE-2021-3760.patch @@ -0,0 +1,33 @@ +From 1b1499a817c90fd1ce9453a2c98d2a01cca0e775 Mon Sep 17 00:00:00 2001 +From: Lin Ma +Date: Thu, 7 Oct 2021 19:44:30 +0200 +Subject: nfc: nci: fix the UAF of rf_conn_info object + +The nci_core_conn_close_rsp_packet() function will release the conn_info +with given conn_id. However, it needs to set the rf_conn_info to NULL to +prevent other routines like nci_rf_intf_activated_ntf_packet() to trigger +the UAF. + +Reviewed-by: Krzysztof Kozlowski +Signed-off-by: Lin Ma +Signed-off-by: Krzysztof Kozlowski +Signed-off-by: David S. Miller +--- + net/nfc/nci/rsp.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c +index a2e72c0038050..b911ab78bed9a 100644 +--- a/net/nfc/nci/rsp.c ++++ b/net/nfc/nci/rsp.c +@@ -334,6 +334,8 @@ static void nci_core_conn_close_rsp_packet(struct nci_dev *ndev, + ndev->cur_conn_id); + if (conn_info) { + list_del(&conn_info->list); ++ if (conn_info == ndev->rf_conn_info) ++ ndev->rf_conn_info = NULL; + devm_kfree(&ndev->nfc_dev->dev, conn_info); + } + } +-- +cgit diff --git a/tests/cases/CVE-2021-38300.patch b/tests/cases/CVE-2021-38300.patch new file mode 100644 index 0000000..f6bad81 --- /dev/null +++ b/tests/cases/CVE-2021-38300.patch @@ -0,0 +1,265 @@ +From 37cb28ec7d3a36a5bace7063a3dba633ab110f8b Mon Sep 17 00:00:00 2001 +From: Piotr Krysiuk +Date: Wed, 15 Sep 2021 17:04:37 +0100 +Subject: bpf, mips: Validate conditional branch offsets + +The conditional branch instructions on MIPS use 18-bit signed offsets +allowing for a branch range of 128 KBytes (backward and forward). +However, this limit is not observed by the cBPF JIT compiler, and so +the JIT compiler emits out-of-range branches when translating certain +cBPF programs. A specific example of such a cBPF program is included in +the "BPF_MAXINSNS: exec all MSH" test from lib/test_bpf.c that executes +anomalous machine code containing incorrect branch offsets under JIT. + +Furthermore, this issue can be abused to craft undesirable machine +code, where the control flow is hijacked to execute arbitrary Kernel +code. + +The following steps can be used to reproduce the issue: + + # echo 1 > /proc/sys/net/core/bpf_jit_enable + # modprobe test_bpf test_name="BPF_MAXINSNS: exec all MSH" + +This should produce multiple warnings from build_bimm() similar to: + + ------------[ cut here ]------------ + WARNING: CPU: 0 PID: 209 at arch/mips/mm/uasm-mips.c:210 build_insn+0x558/0x590 + Micro-assembler field overflow + Modules linked in: test_bpf(+) + CPU: 0 PID: 209 Comm: modprobe Not tainted 5.14.3 #1 + Stack : 00000000 807bb824 82b33c9c 801843c0 00000000 00000004 00000000 63c9b5ee + 82b33af4 80999898 80910000 80900000 82fd6030 00000001 82b33a98 82087180 + 00000000 00000000 80873b28 00000000 000000fc 82b3394c 00000000 2e34312e + 6d6d6f43 809a180f 809a1836 6f6d203a 80900000 00000001 82b33bac 80900000 + 00027f80 00000000 00000000 807bb824 00000000 804ed790 001cc317 00000001 + [...] + Call Trace: + [<80108f44>] show_stack+0x38/0x118 + [<807a7aac>] dump_stack_lvl+0x5c/0x7c + [<807a4b3c>] __warn+0xcc/0x140 + [<807a4c3c>] warn_slowpath_fmt+0x8c/0xb8 + [<8011e198>] build_insn+0x558/0x590 + [<8011e358>] uasm_i_bne+0x20/0x2c + [<80127b48>] build_body+0xa58/0x2a94 + [<80129c98>] bpf_jit_compile+0x114/0x1e4 + [<80613fc4>] bpf_prepare_filter+0x2ec/0x4e4 + [<8061423c>] bpf_prog_create+0x80/0xc4 + [] test_bpf_init+0x300/0xba8 [test_bpf] + [<8010051c>] do_one_initcall+0x50/0x1d4 + [<801c5e54>] do_init_module+0x60/0x220 + [<801c8b20>] sys_finit_module+0xc4/0xfc + [<801144d0>] syscall_common+0x34/0x58 + [...] + ---[ end trace a287d9742503c645 ]--- + +Then the anomalous machine code executes: + +=> 0xc0a18000: addiu sp,sp,-16 + 0xc0a18004: sw s3,0(sp) + 0xc0a18008: sw s4,4(sp) + 0xc0a1800c: sw s5,8(sp) + 0xc0a18010: sw ra,12(sp) + 0xc0a18014: move s5,a0 + 0xc0a18018: move s4,zero + 0xc0a1801c: move s3,zero + + # __BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 0) + 0xc0a18020: lui t6,0x8012 + 0xc0a18024: ori t4,t6,0x9e14 + 0xc0a18028: li a1,0 + 0xc0a1802c: jalr t4 + 0xc0a18030: move a0,s5 + 0xc0a18034: bnez v0,0xc0a1ffb8 # incorrect branch offset + 0xc0a18038: move v0,zero + 0xc0a1803c: andi s4,s3,0xf + 0xc0a18040: b 0xc0a18048 + 0xc0a18044: sll s4,s4,0x2 + [...] + + # __BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 0) + 0xc0a1ffa0: lui t6,0x8012 + 0xc0a1ffa4: ori t4,t6,0x9e14 + 0xc0a1ffa8: li a1,0 + 0xc0a1ffac: jalr t4 + 0xc0a1ffb0: move a0,s5 + 0xc0a1ffb4: bnez v0,0xc0a1ffb8 # incorrect branch offset + 0xc0a1ffb8: move v0,zero + 0xc0a1ffbc: andi s4,s3,0xf + 0xc0a1ffc0: b 0xc0a1ffc8 + 0xc0a1ffc4: sll s4,s4,0x2 + + # __BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 0) + 0xc0a1ffc8: lui t6,0x8012 + 0xc0a1ffcc: ori t4,t6,0x9e14 + 0xc0a1ffd0: li a1,0 + 0xc0a1ffd4: jalr t4 + 0xc0a1ffd8: move a0,s5 + 0xc0a1ffdc: bnez v0,0xc0a3ffb8 # correct branch offset + 0xc0a1ffe0: move v0,zero + 0xc0a1ffe4: andi s4,s3,0xf + 0xc0a1ffe8: b 0xc0a1fff0 + 0xc0a1ffec: sll s4,s4,0x2 + [...] + + # epilogue + 0xc0a3ffb8: lw s3,0(sp) + 0xc0a3ffbc: lw s4,4(sp) + 0xc0a3ffc0: lw s5,8(sp) + 0xc0a3ffc4: lw ra,12(sp) + 0xc0a3ffc8: addiu sp,sp,16 + 0xc0a3ffcc: jr ra + 0xc0a3ffd0: nop + +To mitigate this issue, we assert the branch ranges for each emit call +that could generate an out-of-range branch. + +Fixes: 36366e367ee9 ("MIPS: BPF: Restore MIPS32 cBPF JIT") +Fixes: c6610de353da ("MIPS: net: Add BPF JIT") +Signed-off-by: Piotr Krysiuk +Signed-off-by: Daniel Borkmann +Tested-by: Johan Almbladh +Acked-by: Johan Almbladh +Cc: Paul Burton +Cc: Thomas Bogendoerfer +Link: https://lore.kernel.org/bpf/20210915160437.4080-1-piotras@gmail.com +--- + arch/mips/net/bpf_jit.c | 57 +++++++++++++++++++++++++++++++++++++------------ + 1 file changed, 43 insertions(+), 14 deletions(-) + +diff --git a/arch/mips/net/bpf_jit.c b/arch/mips/net/bpf_jit.c +index 0af88622c6192..cb6d22439f71b 100644 +--- a/arch/mips/net/bpf_jit.c ++++ b/arch/mips/net/bpf_jit.c +@@ -662,6 +662,11 @@ static void build_epilogue(struct jit_ctx *ctx) + ((int)K < 0 ? ((int)K >= SKF_LL_OFF ? func##_negative : func) : \ + func##_positive) + ++static bool is_bad_offset(int b_off) ++{ ++ return b_off > 0x1ffff || b_off < -0x20000; ++} ++ + static int build_body(struct jit_ctx *ctx) + { + const struct bpf_prog *prog = ctx->skf; +@@ -728,7 +733,10 @@ load_common: + /* Load return register on DS for failures */ + emit_reg_move(r_ret, r_zero, ctx); + /* Return with error */ +- emit_b(b_imm(prog->len, ctx), ctx); ++ b_off = b_imm(prog->len, ctx); ++ if (is_bad_offset(b_off)) ++ return -E2BIG; ++ emit_b(b_off, ctx); + emit_nop(ctx); + break; + case BPF_LD | BPF_W | BPF_IND: +@@ -775,8 +783,10 @@ load_ind: + emit_jalr(MIPS_R_RA, r_s0, ctx); + emit_reg_move(MIPS_R_A0, r_skb, ctx); /* delay slot */ + /* Check the error value */ +- emit_bcond(MIPS_COND_NE, r_ret, 0, +- b_imm(prog->len, ctx), ctx); ++ b_off = b_imm(prog->len, ctx); ++ if (is_bad_offset(b_off)) ++ return -E2BIG; ++ emit_bcond(MIPS_COND_NE, r_ret, 0, b_off, ctx); + emit_reg_move(r_ret, r_zero, ctx); + /* We are good */ + /* X <- P[1:K] & 0xf */ +@@ -855,8 +865,10 @@ load_ind: + /* A /= X */ + ctx->flags |= SEEN_X | SEEN_A; + /* Check if r_X is zero */ +- emit_bcond(MIPS_COND_EQ, r_X, r_zero, +- b_imm(prog->len, ctx), ctx); ++ b_off = b_imm(prog->len, ctx); ++ if (is_bad_offset(b_off)) ++ return -E2BIG; ++ emit_bcond(MIPS_COND_EQ, r_X, r_zero, b_off, ctx); + emit_load_imm(r_ret, 0, ctx); /* delay slot */ + emit_div(r_A, r_X, ctx); + break; +@@ -864,8 +876,10 @@ load_ind: + /* A %= X */ + ctx->flags |= SEEN_X | SEEN_A; + /* Check if r_X is zero */ +- emit_bcond(MIPS_COND_EQ, r_X, r_zero, +- b_imm(prog->len, ctx), ctx); ++ b_off = b_imm(prog->len, ctx); ++ if (is_bad_offset(b_off)) ++ return -E2BIG; ++ emit_bcond(MIPS_COND_EQ, r_X, r_zero, b_off, ctx); + emit_load_imm(r_ret, 0, ctx); /* delay slot */ + emit_mod(r_A, r_X, ctx); + break; +@@ -926,7 +940,10 @@ load_ind: + break; + case BPF_JMP | BPF_JA: + /* pc += K */ +- emit_b(b_imm(i + k + 1, ctx), ctx); ++ b_off = b_imm(i + k + 1, ctx); ++ if (is_bad_offset(b_off)) ++ return -E2BIG; ++ emit_b(b_off, ctx); + emit_nop(ctx); + break; + case BPF_JMP | BPF_JEQ | BPF_K: +@@ -1056,12 +1073,16 @@ jmp_cmp: + break; + case BPF_RET | BPF_A: + ctx->flags |= SEEN_A; +- if (i != prog->len - 1) ++ if (i != prog->len - 1) { + /* + * If this is not the last instruction + * then jump to the epilogue + */ +- emit_b(b_imm(prog->len, ctx), ctx); ++ b_off = b_imm(prog->len, ctx); ++ if (is_bad_offset(b_off)) ++ return -E2BIG; ++ emit_b(b_off, ctx); ++ } + emit_reg_move(r_ret, r_A, ctx); /* delay slot */ + break; + case BPF_RET | BPF_K: +@@ -1075,7 +1096,10 @@ jmp_cmp: + * If this is not the last instruction + * then jump to the epilogue + */ +- emit_b(b_imm(prog->len, ctx), ctx); ++ b_off = b_imm(prog->len, ctx); ++ if (is_bad_offset(b_off)) ++ return -E2BIG; ++ emit_b(b_off, ctx); + emit_nop(ctx); + } + break; +@@ -1133,8 +1157,10 @@ jmp_cmp: + /* Load *dev pointer */ + emit_load_ptr(r_s0, r_skb, off, ctx); + /* error (0) in the delay slot */ +- emit_bcond(MIPS_COND_EQ, r_s0, r_zero, +- b_imm(prog->len, ctx), ctx); ++ b_off = b_imm(prog->len, ctx); ++ if (is_bad_offset(b_off)) ++ return -E2BIG; ++ emit_bcond(MIPS_COND_EQ, r_s0, r_zero, b_off, ctx); + emit_reg_move(r_ret, r_zero, ctx); + if (code == (BPF_ANC | SKF_AD_IFINDEX)) { + BUILD_BUG_ON(sizeof_field(struct net_device, ifindex) != 4); +@@ -1244,7 +1270,10 @@ void bpf_jit_compile(struct bpf_prog *fp) + + /* Generate the actual JIT code */ + build_prologue(&ctx); +- build_body(&ctx); ++ if (build_body(&ctx)) { ++ module_memfree(ctx.target); ++ goto out; ++ } + build_epilogue(&ctx); + + /* Update the icache */ +-- +cgit diff --git a/tests/cases/CVE-2021-40490.patch b/tests/cases/CVE-2021-40490.patch new file mode 100644 index 0000000..a1a0ffb --- /dev/null +++ b/tests/cases/CVE-2021-40490.patch @@ -0,0 +1,42 @@ +From a54c4613dac1500b40e4ab55199f7c51f028e848 Mon Sep 17 00:00:00 2001 +From: Theodore Ts'o +Date: Fri, 20 Aug 2021 23:44:17 -0400 +Subject: ext4: fix race writing to an inline_data file while its xattrs are + changing + +The location of the system.data extended attribute can change whenever +xattr_sem is not taken. So we need to recalculate the i_inline_off +field since it mgiht have changed between ext4_write_begin() and +ext4_write_end(). + +This means that caching i_inline_off is probably not helpful, so in +the long run we should probably get rid of it and shrink the in-memory +ext4 inode slightly, but let's fix the race the simple way for now. + +Cc: stable@kernel.org +Fixes: f19d5870cbf72 ("ext4: add normal write support for inline data") +Reported-by: syzbot+13146364637c7363a7de@syzkaller.appspotmail.com +Signed-off-by: Theodore Ts'o +--- + fs/ext4/inline.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c +index 70cb64db33f73..24e994e75f5ca 100644 +--- a/fs/ext4/inline.c ++++ b/fs/ext4/inline.c +@@ -750,6 +750,12 @@ int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len, + ext4_write_lock_xattr(inode, &no_expand); + BUG_ON(!ext4_has_inline_data(inode)); + ++ /* ++ * ei->i_inline_off may have changed since ext4_write_begin() ++ * called ext4_try_to_write_inline_data() ++ */ ++ (void) ext4_find_inline_data_nolock(inode); ++ + kaddr = kmap_atomic(page); + ext4_write_inline_data(inode, &iloc, kaddr, pos, len); + kunmap_atomic(kaddr); +-- +cgit diff --git a/tests/cases/CVE-2021-41073.patch b/tests/cases/CVE-2021-41073.patch new file mode 100644 index 0000000..026090b --- /dev/null +++ b/tests/cases/CVE-2021-41073.patch @@ -0,0 +1,44 @@ +From 16c8d2df7ec0eed31b7d3b61cb13206a7fb930cc Mon Sep 17 00:00:00 2001 +From: Jens Axboe +Date: Sun, 12 Sep 2021 06:45:07 -0600 +Subject: io_uring: ensure symmetry in handling iter types in loop_rw_iter() + +When setting up the next segment, we check what type the iter is and +handle it accordingly. However, when incrementing and processed amount +we do not, and both iter advance and addr/len are adjusted, regardless +of type. Split the increment side just like we do on the setup side. + +Fixes: 4017eb91a9e7 ("io_uring: make loop_rw_iter() use original user supplied pointers") +Cc: stable@vger.kernel.org +Reported-by: Valentina Palmiotti +Reviewed-by: Pavel Begunkov +Signed-off-by: Jens Axboe +--- + fs/io_uring.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/fs/io_uring.c b/fs/io_uring.c +index 16fb7436043c2..66a7414c37568 100644 +--- a/fs/io_uring.c ++++ b/fs/io_uring.c +@@ -3263,12 +3263,15 @@ static ssize_t loop_rw_iter(int rw, struct io_kiocb *req, struct iov_iter *iter) + ret = nr; + break; + } ++ if (!iov_iter_is_bvec(iter)) { ++ iov_iter_advance(iter, nr); ++ } else { ++ req->rw.len -= nr; ++ req->rw.addr += nr; ++ } + ret += nr; + if (nr != iovec.iov_len) + break; +- req->rw.len -= nr; +- req->rw.addr += nr; +- iov_iter_advance(iter, nr); + } + + return ret; +-- +cgit diff --git a/tests/cases/CVE-2021-4154.patch b/tests/cases/CVE-2021-4154.patch new file mode 100644 index 0000000..8e0e176 --- /dev/null +++ b/tests/cases/CVE-2021-4154.patch @@ -0,0 +1,63 @@ +From 3b0462726e7ef281c35a7a4ae33e93ee2bc9975b Mon Sep 17 00:00:00 2001 +From: Christian Brauner +Date: Wed, 14 Jul 2021 15:47:49 +0200 +Subject: cgroup: verify that source is a string + +The following sequence can be used to trigger a UAF: + + int fscontext_fd = fsopen("cgroup"); + int fd_null = open("/dev/null, O_RDONLY); + int fsconfig(fscontext_fd, FSCONFIG_SET_FD, "source", fd_null); + close_range(3, ~0U, 0); + +The cgroup v1 specific fs parser expects a string for the "source" +parameter. However, it is perfectly legitimate to e.g. specify a file +descriptor for the "source" parameter. The fs parser doesn't know what +a filesystem allows there. So it's a bug to assume that "source" is +always of type fs_value_is_string when it can reasonably also be +fs_value_is_file. + +This assumption in the cgroup code causes a UAF because struct +fs_parameter uses a union for the actual value. Access to that union is +guarded by the param->type member. Since the cgroup paramter parser +didn't check param->type but unconditionally moved param->string into +fc->source a close on the fscontext_fd would trigger a UAF during +put_fs_context() which frees fc->source thereby freeing the file stashed +in param->file causing a UAF during a close of the fd_null. + +Fix this by verifying that param->type is actually a string and report +an error if not. + +In follow up patches I'll add a new generic helper that can be used here +and by other filesystems instead of this error-prone copy-pasta fix. +But fixing it in here first makes backporting a it to stable a lot +easier. + +Fixes: 8d2451f4994f ("cgroup1: switch to option-by-option parsing") +Reported-by: syzbot+283ce5a46486d6acdbaf@syzkaller.appspotmail.com +Cc: Christoph Hellwig +Cc: Alexander Viro +Cc: Dmitry Vyukov +Cc: +Cc: syzkaller-bugs +Signed-off-by: Christian Brauner +Signed-off-by: Linus Torvalds +--- + kernel/cgroup/cgroup-v1.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c +index ee93b6e895874..527917c0b30be 100644 +--- a/kernel/cgroup/cgroup-v1.c ++++ b/kernel/cgroup/cgroup-v1.c +@@ -912,6 +912,8 @@ int cgroup1_parse_param(struct fs_context *fc, struct fs_parameter *param) + opt = fs_parse(fc, cgroup1_fs_parameters, param, &result); + if (opt == -ENOPARAM) { + if (strcmp(param->key, "source") == 0) { ++ if (param->type != fs_value_is_string) ++ return invalf(fc, "Non-string source"); + if (fc->source) + return invalf(fc, "Multiple sources not supported"); + fc->source = param->string; +-- +cgit diff --git a/tests/cases/CVE-2021-42008.patch b/tests/cases/CVE-2021-42008.patch new file mode 100644 index 0000000..ad02259 --- /dev/null +++ b/tests/cases/CVE-2021-42008.patch @@ -0,0 +1,61 @@ +From 19d1532a187669ce86d5a2696eb7275310070793 Mon Sep 17 00:00:00 2001 +From: Pavel Skripkin +Date: Fri, 13 Aug 2021 18:14:33 +0300 +Subject: net: 6pack: fix slab-out-of-bounds in decode_data + +Syzbot reported slab-out-of bounds write in decode_data(). +The problem was in missing validation checks. + +Syzbot's reproducer generated malicious input, which caused +decode_data() to be called a lot in sixpack_decode(). Since +rx_count_cooked is only 400 bytes and noone reported before, +that 400 bytes is not enough, let's just check if input is malicious +and complain about buffer overrun. + +Fail log: +================================================================== +BUG: KASAN: slab-out-of-bounds in drivers/net/hamradio/6pack.c:843 +Write of size 1 at addr ffff888087c5544e by task kworker/u4:0/7 + +CPU: 0 PID: 7 Comm: kworker/u4:0 Not tainted 5.6.0-rc3-syzkaller #0 +... +Workqueue: events_unbound flush_to_ldisc +Call Trace: + __dump_stack lib/dump_stack.c:77 [inline] + dump_stack+0x197/0x210 lib/dump_stack.c:118 + print_address_description.constprop.0.cold+0xd4/0x30b mm/kasan/report.c:374 + __kasan_report.cold+0x1b/0x32 mm/kasan/report.c:506 + kasan_report+0x12/0x20 mm/kasan/common.c:641 + __asan_report_store1_noabort+0x17/0x20 mm/kasan/generic_report.c:137 + decode_data.part.0+0x23b/0x270 drivers/net/hamradio/6pack.c:843 + decode_data drivers/net/hamradio/6pack.c:965 [inline] + sixpack_decode drivers/net/hamradio/6pack.c:968 [inline] + +Reported-and-tested-by: syzbot+fc8cd9a673d4577fb2e4@syzkaller.appspotmail.com +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Signed-off-by: Pavel Skripkin +Reviewed-by: Dan Carpenter +Signed-off-by: David S. Miller +--- + drivers/net/hamradio/6pack.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c +index fcf3af76b6d7b..8fe8887d506a3 100644 +--- a/drivers/net/hamradio/6pack.c ++++ b/drivers/net/hamradio/6pack.c +@@ -827,6 +827,12 @@ static void decode_data(struct sixpack *sp, unsigned char inbyte) + return; + } + ++ if (sp->rx_count_cooked + 2 >= sizeof(sp->cooked_buf)) { ++ pr_err("6pack: cooked buffer overrun, data loss\n"); ++ sp->rx_count = 0; ++ return; ++ } ++ + buf = sp->raw_buf; + sp->cooked_buf[sp->rx_count_cooked++] = + buf[0] | ((buf[1] << 2) & 0xc0); +-- +cgit diff --git a/tests/cases/CVE-2021-43267.patch b/tests/cases/CVE-2021-43267.patch new file mode 100644 index 0000000..c5b393b --- /dev/null +++ b/tests/cases/CVE-2021-43267.patch @@ -0,0 +1,92 @@ +From fa40d9734a57bcbfa79a280189799f76c88f7bb0 Mon Sep 17 00:00:00 2001 +From: Max VA +Date: Mon, 25 Oct 2021 17:31:53 +0200 +Subject: tipc: fix size validations for the MSG_CRYPTO type + +The function tipc_crypto_key_rcv is used to parse MSG_CRYPTO messages +to receive keys from other nodes in the cluster in order to decrypt any +further messages from them. +This patch verifies that any supplied sizes in the message body are +valid for the received message. + +Fixes: 1ef6f7c9390f ("tipc: add automatic session key exchange") +Signed-off-by: Max VA +Acked-by: Ying Xue +Signed-off-by: Greg Kroah-Hartman +Acked-by: Jon Maloy +Signed-off-by: David S. Miller +--- + net/tipc/crypto.c | 32 +++++++++++++++++++++----------- + 1 file changed, 21 insertions(+), 11 deletions(-) + +diff --git a/net/tipc/crypto.c b/net/tipc/crypto.c +index c9391d38de85cc..dc60c32bb70df9 100644 +--- a/net/tipc/crypto.c ++++ b/net/tipc/crypto.c +@@ -2285,43 +2285,53 @@ static bool tipc_crypto_key_rcv(struct tipc_crypto *rx, struct tipc_msg *hdr) + u16 key_gen = msg_key_gen(hdr); + u16 size = msg_data_sz(hdr); + u8 *data = msg_data(hdr); ++ unsigned int keylen; ++ ++ /* Verify whether the size can exist in the packet */ ++ if (unlikely(size < sizeof(struct tipc_aead_key) + TIPC_AEAD_KEYLEN_MIN)) { ++ pr_debug("%s: message data size is too small\n", rx->name); ++ goto exit; ++ } ++ ++ keylen = ntohl(*((__be32 *)(data + TIPC_AEAD_ALG_NAME))); ++ ++ /* Verify the supplied size values */ ++ if (unlikely(size != keylen + sizeof(struct tipc_aead_key) || ++ keylen > TIPC_AEAD_KEY_SIZE_MAX)) { ++ pr_debug("%s: invalid MSG_CRYPTO key size\n", rx->name); ++ goto exit; ++ } + + spin_lock(&rx->lock); + if (unlikely(rx->skey || (key_gen == rx->key_gen && rx->key.keys))) { + pr_err("%s: key existed <%p>, gen %d vs %d\n", rx->name, + rx->skey, key_gen, rx->key_gen); +- goto exit; ++ goto exit_unlock; + } + + /* Allocate memory for the key */ + skey = kmalloc(size, GFP_ATOMIC); + if (unlikely(!skey)) { + pr_err("%s: unable to allocate memory for skey\n", rx->name); +- goto exit; ++ goto exit_unlock; + } + + /* Copy key from msg data */ +- skey->keylen = ntohl(*((__be32 *)(data + TIPC_AEAD_ALG_NAME))); ++ skey->keylen = keylen; + memcpy(skey->alg_name, data, TIPC_AEAD_ALG_NAME); + memcpy(skey->key, data + TIPC_AEAD_ALG_NAME + sizeof(__be32), + skey->keylen); + +- /* Sanity check */ +- if (unlikely(size != tipc_aead_key_size(skey))) { +- kfree(skey); +- skey = NULL; +- goto exit; +- } +- + rx->key_gen = key_gen; + rx->skey_mode = msg_key_mode(hdr); + rx->skey = skey; + rx->nokey = 0; + mb(); /* for nokey flag */ + +-exit: ++exit_unlock: + spin_unlock(&rx->lock); + ++exit: + /* Schedule the key attaching on this crypto */ + if (likely(skey && queue_delayed_work(tx->wq, &rx->work, 0))) + return true; +-- +cgit diff --git a/tests/cases/CVE-2021-44733.patch b/tests/cases/CVE-2021-44733.patch new file mode 100644 index 0000000..1456ea9 --- /dev/null +++ b/tests/cases/CVE-2021-44733.patch @@ -0,0 +1,346 @@ +From dfd0743f1d9ea76931510ed150334d571fbab49d Mon Sep 17 00:00:00 2001 +From: Jens Wiklander +Date: Thu, 9 Dec 2021 15:59:37 +0100 +Subject: tee: handle lookup of shm with reference count 0 + +Since the tee subsystem does not keep a strong reference to its idle +shared memory buffers, it races with other threads that try to destroy a +shared memory through a close of its dma-buf fd or by unmapping the +memory. + +In tee_shm_get_from_id() when a lookup in teedev->idr has been +successful, it is possible that the tee_shm is in the dma-buf teardown +path, but that path is blocked by the teedev mutex. Since we don't have +an API to tell if the tee_shm is in the dma-buf teardown path or not we +must find another way of detecting this condition. + +Fix this by doing the reference counting directly on the tee_shm using a +new refcount_t refcount field. dma-buf is replaced by using +anon_inode_getfd() instead, this separates the life-cycle of the +underlying file from the tee_shm. tee_shm_put() is updated to hold the +mutex when decreasing the refcount to 0 and then remove the tee_shm from +teedev->idr before releasing the mutex. This means that the tee_shm can +never be found unless it has a refcount larger than 0. + +Fixes: 967c9cca2cc5 ("tee: generic TEE subsystem") +Cc: stable@vger.kernel.org +Reviewed-by: Greg Kroah-Hartman +Reviewed-by: Lars Persson +Reviewed-by: Sumit Garg +Reported-by: Patrik Lantz +Signed-off-by: Jens Wiklander +--- + drivers/tee/tee_shm.c | 174 ++++++++++++++++++------------------------------ + include/linux/tee_drv.h | 4 +- + 2 files changed, 68 insertions(+), 110 deletions(-) + +diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c +index 8a8deb95e918e..499fccba3d74b 100644 +--- a/drivers/tee/tee_shm.c ++++ b/drivers/tee/tee_shm.c +@@ -1,20 +1,17 @@ + // SPDX-License-Identifier: GPL-2.0-only + /* +- * Copyright (c) 2015-2016, Linaro Limited ++ * Copyright (c) 2015-2017, 2019-2021 Linaro Limited + */ ++#include + #include +-#include +-#include + #include ++#include + #include + #include + #include + #include +-#include + #include "tee_private.h" + +-MODULE_IMPORT_NS(DMA_BUF); +- + static void release_registered_pages(struct tee_shm *shm) + { + if (shm->pages) { +@@ -31,16 +28,8 @@ static void release_registered_pages(struct tee_shm *shm) + } + } + +-static void tee_shm_release(struct tee_shm *shm) ++static void tee_shm_release(struct tee_device *teedev, struct tee_shm *shm) + { +- struct tee_device *teedev = shm->ctx->teedev; +- +- if (shm->flags & TEE_SHM_DMA_BUF) { +- mutex_lock(&teedev->mutex); +- idr_remove(&teedev->idr, shm->id); +- mutex_unlock(&teedev->mutex); +- } +- + if (shm->flags & TEE_SHM_POOL) { + struct tee_shm_pool_mgr *poolm; + +@@ -67,45 +56,6 @@ static void tee_shm_release(struct tee_shm *shm) + tee_device_put(teedev); + } + +-static struct sg_table *tee_shm_op_map_dma_buf(struct dma_buf_attachment +- *attach, enum dma_data_direction dir) +-{ +- return NULL; +-} +- +-static void tee_shm_op_unmap_dma_buf(struct dma_buf_attachment *attach, +- struct sg_table *table, +- enum dma_data_direction dir) +-{ +-} +- +-static void tee_shm_op_release(struct dma_buf *dmabuf) +-{ +- struct tee_shm *shm = dmabuf->priv; +- +- tee_shm_release(shm); +-} +- +-static int tee_shm_op_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) +-{ +- struct tee_shm *shm = dmabuf->priv; +- size_t size = vma->vm_end - vma->vm_start; +- +- /* Refuse sharing shared memory provided by application */ +- if (shm->flags & TEE_SHM_USER_MAPPED) +- return -EINVAL; +- +- return remap_pfn_range(vma, vma->vm_start, shm->paddr >> PAGE_SHIFT, +- size, vma->vm_page_prot); +-} +- +-static const struct dma_buf_ops tee_shm_dma_buf_ops = { +- .map_dma_buf = tee_shm_op_map_dma_buf, +- .unmap_dma_buf = tee_shm_op_unmap_dma_buf, +- .release = tee_shm_op_release, +- .mmap = tee_shm_op_mmap, +-}; +- + struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags) + { + struct tee_device *teedev = ctx->teedev; +@@ -140,6 +90,7 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags) + goto err_dev_put; + } + ++ refcount_set(&shm->refcount, 1); + shm->flags = flags | TEE_SHM_POOL; + shm->ctx = ctx; + if (flags & TEE_SHM_DMA_BUF) +@@ -153,10 +104,7 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags) + goto err_kfree; + } + +- + if (flags & TEE_SHM_DMA_BUF) { +- DEFINE_DMA_BUF_EXPORT_INFO(exp_info); +- + mutex_lock(&teedev->mutex); + shm->id = idr_alloc(&teedev->idr, shm, 1, 0, GFP_KERNEL); + mutex_unlock(&teedev->mutex); +@@ -164,28 +112,11 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags) + ret = ERR_PTR(shm->id); + goto err_pool_free; + } +- +- exp_info.ops = &tee_shm_dma_buf_ops; +- exp_info.size = shm->size; +- exp_info.flags = O_RDWR; +- exp_info.priv = shm; +- +- shm->dmabuf = dma_buf_export(&exp_info); +- if (IS_ERR(shm->dmabuf)) { +- ret = ERR_CAST(shm->dmabuf); +- goto err_rem; +- } + } + + teedev_ctx_get(ctx); + + return shm; +-err_rem: +- if (flags & TEE_SHM_DMA_BUF) { +- mutex_lock(&teedev->mutex); +- idr_remove(&teedev->idr, shm->id); +- mutex_unlock(&teedev->mutex); +- } + err_pool_free: + poolm->ops->free(poolm, shm); + err_kfree: +@@ -246,6 +177,7 @@ struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr, + goto err; + } + ++ refcount_set(&shm->refcount, 1); + shm->flags = flags | TEE_SHM_REGISTER; + shm->ctx = ctx; + shm->id = -1; +@@ -306,22 +238,6 @@ struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr, + goto err; + } + +- if (flags & TEE_SHM_DMA_BUF) { +- DEFINE_DMA_BUF_EXPORT_INFO(exp_info); +- +- exp_info.ops = &tee_shm_dma_buf_ops; +- exp_info.size = shm->size; +- exp_info.flags = O_RDWR; +- exp_info.priv = shm; +- +- shm->dmabuf = dma_buf_export(&exp_info); +- if (IS_ERR(shm->dmabuf)) { +- ret = ERR_CAST(shm->dmabuf); +- teedev->desc->ops->shm_unregister(ctx, shm); +- goto err; +- } +- } +- + return shm; + err: + if (shm) { +@@ -339,6 +255,35 @@ err: + } + EXPORT_SYMBOL_GPL(tee_shm_register); + ++static int tee_shm_fop_release(struct inode *inode, struct file *filp) ++{ ++ tee_shm_put(filp->private_data); ++ return 0; ++} ++ ++static int tee_shm_fop_mmap(struct file *filp, struct vm_area_struct *vma) ++{ ++ struct tee_shm *shm = filp->private_data; ++ size_t size = vma->vm_end - vma->vm_start; ++ ++ /* Refuse sharing shared memory provided by application */ ++ if (shm->flags & TEE_SHM_USER_MAPPED) ++ return -EINVAL; ++ ++ /* check for overflowing the buffer's size */ ++ if (vma->vm_pgoff + vma_pages(vma) > shm->size >> PAGE_SHIFT) ++ return -EINVAL; ++ ++ return remap_pfn_range(vma, vma->vm_start, shm->paddr >> PAGE_SHIFT, ++ size, vma->vm_page_prot); ++} ++ ++static const struct file_operations tee_shm_fops = { ++ .owner = THIS_MODULE, ++ .release = tee_shm_fop_release, ++ .mmap = tee_shm_fop_mmap, ++}; ++ + /** + * tee_shm_get_fd() - Increase reference count and return file descriptor + * @shm: Shared memory handle +@@ -351,10 +296,11 @@ int tee_shm_get_fd(struct tee_shm *shm) + if (!(shm->flags & TEE_SHM_DMA_BUF)) + return -EINVAL; + +- get_dma_buf(shm->dmabuf); +- fd = dma_buf_fd(shm->dmabuf, O_CLOEXEC); ++ /* matched by tee_shm_put() in tee_shm_op_release() */ ++ refcount_inc(&shm->refcount); ++ fd = anon_inode_getfd("tee_shm", &tee_shm_fops, shm, O_RDWR); + if (fd < 0) +- dma_buf_put(shm->dmabuf); ++ tee_shm_put(shm); + return fd; + } + +@@ -364,17 +310,7 @@ int tee_shm_get_fd(struct tee_shm *shm) + */ + void tee_shm_free(struct tee_shm *shm) + { +- /* +- * dma_buf_put() decreases the dmabuf reference counter and will +- * call tee_shm_release() when the last reference is gone. +- * +- * In the case of driver private memory we call tee_shm_release +- * directly instead as it doesn't have a reference counter. +- */ +- if (shm->flags & TEE_SHM_DMA_BUF) +- dma_buf_put(shm->dmabuf); +- else +- tee_shm_release(shm); ++ tee_shm_put(shm); + } + EXPORT_SYMBOL_GPL(tee_shm_free); + +@@ -481,10 +417,15 @@ struct tee_shm *tee_shm_get_from_id(struct tee_context *ctx, int id) + teedev = ctx->teedev; + mutex_lock(&teedev->mutex); + shm = idr_find(&teedev->idr, id); ++ /* ++ * If the tee_shm was found in the IDR it must have a refcount ++ * larger than 0 due to the guarantee in tee_shm_put() below. So ++ * it's safe to use refcount_inc(). ++ */ + if (!shm || shm->ctx != ctx) + shm = ERR_PTR(-EINVAL); +- else if (shm->flags & TEE_SHM_DMA_BUF) +- get_dma_buf(shm->dmabuf); ++ else ++ refcount_inc(&shm->refcount); + mutex_unlock(&teedev->mutex); + return shm; + } +@@ -496,7 +437,24 @@ EXPORT_SYMBOL_GPL(tee_shm_get_from_id); + */ + void tee_shm_put(struct tee_shm *shm) + { +- if (shm->flags & TEE_SHM_DMA_BUF) +- dma_buf_put(shm->dmabuf); ++ struct tee_device *teedev = shm->ctx->teedev; ++ bool do_release = false; ++ ++ mutex_lock(&teedev->mutex); ++ if (refcount_dec_and_test(&shm->refcount)) { ++ /* ++ * refcount has reached 0, we must now remove it from the ++ * IDR before releasing the mutex. This will guarantee that ++ * the refcount_inc() in tee_shm_get_from_id() never starts ++ * from 0. ++ */ ++ if (shm->flags & TEE_SHM_DMA_BUF) ++ idr_remove(&teedev->idr, shm->id); ++ do_release = true; ++ } ++ mutex_unlock(&teedev->mutex); ++ ++ if (do_release) ++ tee_shm_release(teedev, shm); + } + EXPORT_SYMBOL_GPL(tee_shm_put); +diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h +index a1f03461369bd..cf5999626e28d 100644 +--- a/include/linux/tee_drv.h ++++ b/include/linux/tee_drv.h +@@ -195,7 +195,7 @@ int tee_session_calc_client_uuid(uuid_t *uuid, u32 connection_method, + * @offset: offset of buffer in user space + * @pages: locked pages from userspace + * @num_pages: number of locked pages +- * @dmabuf: dmabuf used to for exporting to user space ++ * @refcount: reference counter + * @flags: defined by TEE_SHM_* in tee_drv.h + * @id: unique id of a shared memory object on this device, shared + * with user space +@@ -214,7 +214,7 @@ struct tee_shm { + unsigned int offset; + struct page **pages; + size_t num_pages; +- struct dma_buf *dmabuf; ++ refcount_t refcount; + u32 flags; + int id; + u64 sec_world_id; +-- +cgit diff --git a/tests/cases/CVE-2022-0185.patch b/tests/cases/CVE-2022-0185.patch new file mode 100644 index 0000000..367f7e0 --- /dev/null +++ b/tests/cases/CVE-2022-0185.patch @@ -0,0 +1,37 @@ +From 722d94847de29310e8aa03fcbdb41fc92c521756 Mon Sep 17 00:00:00 2001 +From: Jamie Hill-Daniel +Date: Tue, 18 Jan 2022 08:06:04 +0100 +Subject: vfs: fs_context: fix up param length parsing in legacy_parse_param + +The "PAGE_SIZE - 2 - size" calculation in legacy_parse_param() is an +unsigned type so a large value of "size" results in a high positive +value instead of a negative value as expected. Fix this by getting rid +of the subtraction. + +Signed-off-by: Jamie Hill-Daniel +Signed-off-by: William Liu +Tested-by: Salvatore Bonaccorso +Tested-by: Thadeu Lima de Souza Cascardo +Acked-by: Dan Carpenter +Acked-by: Al Viro +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Linus Torvalds +--- + fs/fs_context.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/fs_context.c b/fs/fs_context.c +index b7e43a780a625..24ce12f0db32e 100644 +--- a/fs/fs_context.c ++++ b/fs/fs_context.c +@@ -548,7 +548,7 @@ static int legacy_parse_param(struct fs_context *fc, struct fs_parameter *param) + param->key); + } + +- if (len > PAGE_SIZE - 2 - size) ++ if (size + len + 2 > PAGE_SIZE) + return invalf(fc, "VFS: Legacy: Cumulative options too large"); + if (strchr(param->key, ',') || + (param->type == fs_value_is_string && +-- +cgit diff --git a/tests/cases/CVE-2022-0435.patch b/tests/cases/CVE-2022-0435.patch new file mode 100644 index 0000000..8d52aa1 --- /dev/null +++ b/tests/cases/CVE-2022-0435.patch @@ -0,0 +1,85 @@ +From 9aa422ad326634b76309e8ff342c246800621216 Mon Sep 17 00:00:00 2001 +From: Jon Maloy +Date: Sat, 5 Feb 2022 14:11:18 -0500 +Subject: tipc: improve size validations for received domain records + +The function tipc_mon_rcv() allows a node to receive and process +domain_record structs from peer nodes to track their views of the +network topology. + +This patch verifies that the number of members in a received domain +record does not exceed the limit defined by MAX_MON_DOMAIN, something +that may otherwise lead to a stack overflow. + +tipc_mon_rcv() is called from the function tipc_link_proto_rcv(), where +we are reading a 32 bit message data length field into a uint16. To +avert any risk of bit overflow, we add an extra sanity check for this in +that function. We cannot see that happen with the current code, but +future designers being unaware of this risk, may introduce it by +allowing delivery of very large (> 64k) sk buffers from the bearer +layer. This potential problem was identified by Eric Dumazet. + +This fixes CVE-2022-0435 + +Reported-by: Samuel Page +Reported-by: Eric Dumazet +Fixes: 35c55c9877f8 ("tipc: add neighbor monitoring framework") +Signed-off-by: Jon Maloy +Reviewed-by: Xin Long +Reviewed-by: Samuel Page +Reviewed-by: Eric Dumazet +Signed-off-by: Linus Torvalds +--- + net/tipc/link.c | 9 +++++++-- + net/tipc/monitor.c | 2 ++ + 2 files changed, 9 insertions(+), 2 deletions(-) + +diff --git a/net/tipc/link.c b/net/tipc/link.c +index 8d9e09f48f4ca..1e14d7f8f28f1 100644 +--- a/net/tipc/link.c ++++ b/net/tipc/link.c +@@ -2200,7 +2200,7 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb, + struct tipc_msg *hdr = buf_msg(skb); + struct tipc_gap_ack_blks *ga = NULL; + bool reply = msg_probe(hdr), retransmitted = false; +- u16 dlen = msg_data_sz(hdr), glen = 0; ++ u32 dlen = msg_data_sz(hdr), glen = 0; + u16 peers_snd_nxt = msg_next_sent(hdr); + u16 peers_tol = msg_link_tolerance(hdr); + u16 peers_prio = msg_linkprio(hdr); +@@ -2214,6 +2214,10 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb, + void *data; + + trace_tipc_proto_rcv(skb, false, l->name); ++ ++ if (dlen > U16_MAX) ++ goto exit; ++ + if (tipc_link_is_blocked(l) || !xmitq) + goto exit; + +@@ -2309,7 +2313,8 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb, + + /* Receive Gap ACK blocks from peer if any */ + glen = tipc_get_gap_ack_blks(&ga, l, hdr, true); +- ++ if(glen > dlen) ++ break; + tipc_mon_rcv(l->net, data + glen, dlen - glen, l->addr, + &l->mon_state, l->bearer_id); + +diff --git a/net/tipc/monitor.c b/net/tipc/monitor.c +index 407619697292f..2f4d23238a7e3 100644 +--- a/net/tipc/monitor.c ++++ b/net/tipc/monitor.c +@@ -496,6 +496,8 @@ void tipc_mon_rcv(struct net *net, void *data, u16 dlen, u32 addr, + state->probing = false; + + /* Sanity check received domain record */ ++ if (new_member_cnt > MAX_MON_DOMAIN) ++ return; + if (dlen < dom_rec_len(arrv_dom, 0)) + return; + if (dlen != dom_rec_len(arrv_dom, new_member_cnt)) +-- +cgit diff --git a/tests/cases/CVE-2022-0492.patch b/tests/cases/CVE-2022-0492.patch new file mode 100644 index 0000000..373644f --- /dev/null +++ b/tests/cases/CVE-2022-0492.patch @@ -0,0 +1,53 @@ +From 24f6008564183aa120d07c03d9289519c2fe02af Mon Sep 17 00:00:00 2001 +From: "Eric W. Biederman" +Date: Thu, 20 Jan 2022 11:04:01 -0600 +Subject: cgroup-v1: Require capabilities to set release_agent + +The cgroup release_agent is called with call_usermodehelper. The function +call_usermodehelper starts the release_agent with a full set fo capabilities. +Therefore require capabilities when setting the release_agaent. + +Reported-by: Tabitha Sable +Tested-by: Tabitha Sable +Fixes: 81a6a5cdd2c5 ("Task Control Groups: automatic userspace notification of idle cgroups") +Cc: stable@vger.kernel.org # v2.6.24+ +Signed-off-by: "Eric W. Biederman" +Signed-off-by: Tejun Heo +--- + kernel/cgroup/cgroup-v1.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c +index 41e0837a5a0bd..0e877dbcfeea9 100644 +--- a/kernel/cgroup/cgroup-v1.c ++++ b/kernel/cgroup/cgroup-v1.c +@@ -549,6 +549,14 @@ static ssize_t cgroup_release_agent_write(struct kernfs_open_file *of, + + BUILD_BUG_ON(sizeof(cgrp->root->release_agent_path) < PATH_MAX); + ++ /* ++ * Release agent gets called with all capabilities, ++ * require capabilities to set release agent. ++ */ ++ if ((of->file->f_cred->user_ns != &init_user_ns) || ++ !capable(CAP_SYS_ADMIN)) ++ return -EPERM; ++ + cgrp = cgroup_kn_lock_live(of->kn, false); + if (!cgrp) + return -ENODEV; +@@ -954,6 +962,12 @@ int cgroup1_parse_param(struct fs_context *fc, struct fs_parameter *param) + /* Specifying two release agents is forbidden */ + if (ctx->release_agent) + return invalfc(fc, "release_agent respecified"); ++ /* ++ * Release agent gets called with all capabilities, ++ * require capabilities to set release agent. ++ */ ++ if ((fc->user_ns != &init_user_ns) || !capable(CAP_SYS_ADMIN)) ++ return invalfc(fc, "Setting release_agent not allowed"); + ctx->release_agent = param->string; + param->string = NULL; + break; +-- +cgit diff --git a/tests/cases/CVE-2022-0847.patch b/tests/cases/CVE-2022-0847.patch new file mode 100644 index 0000000..eff12f4 --- /dev/null +++ b/tests/cases/CVE-2022-0847.patch @@ -0,0 +1,42 @@ +From 9d2231c5d74e13b2a0546fee6737ee4446017903 Mon Sep 17 00:00:00 2001 +From: Max Kellermann +Date: Mon, 21 Feb 2022 11:03:13 +0100 +Subject: lib/iov_iter: initialize "flags" in new pipe_buffer + +The functions copy_page_to_iter_pipe() and push_pipe() can both +allocate a new pipe_buffer, but the "flags" member initializer is +missing. + +Fixes: 241699cd72a8 ("new iov_iter flavour: pipe-backed") +To: Alexander Viro +To: linux-fsdevel@vger.kernel.org +To: linux-kernel@vger.kernel.org +Cc: stable@vger.kernel.org +Signed-off-by: Max Kellermann +Signed-off-by: Al Viro +--- + lib/iov_iter.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/lib/iov_iter.c b/lib/iov_iter.c +index b0e0acdf96c15e..6dd5330f7a9957 100644 +--- a/lib/iov_iter.c ++++ b/lib/iov_iter.c +@@ -414,6 +414,7 @@ static size_t copy_page_to_iter_pipe(struct page *page, size_t offset, size_t by + return 0; + + buf->ops = &page_cache_pipe_buf_ops; ++ buf->flags = 0; + get_page(page); + buf->page = page; + buf->offset = offset; +@@ -577,6 +578,7 @@ static size_t push_pipe(struct iov_iter *i, size_t size, + break; + + buf->ops = &default_pipe_buf_ops; ++ buf->flags = 0; + buf->page = page; + buf->offset = 0; + buf->len = min_t(ssize_t, left, PAGE_SIZE); +-- +cgit diff --git a/tests/cases/CVE-2022-0995.patch b/tests/cases/CVE-2022-0995.patch new file mode 100644 index 0000000..8ce3282 --- /dev/null +++ b/tests/cases/CVE-2022-0995.patch @@ -0,0 +1,102 @@ +From c993ee0f9f81caf5767a50d1faeba39a0dc82af2 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Fri, 11 Mar 2022 13:23:31 +0000 +Subject: watch_queue: Fix filter limit check + +In watch_queue_set_filter(), there are a couple of places where we check +that the filter type value does not exceed what the type_filter bitmap +can hold. One place calculates the number of bits by: + + if (tf[i].type >= sizeof(wfilter->type_filter) * 8) + +which is fine, but the second does: + + if (tf[i].type >= sizeof(wfilter->type_filter) * BITS_PER_LONG) + +which is not. This can lead to a couple of out-of-bounds writes due to +a too-large type: + + (1) __set_bit() on wfilter->type_filter + (2) Writing more elements in wfilter->filters[] than we allocated. + +Fix this by just using the proper WATCH_TYPE__NR instead, which is the +number of types we actually know about. + +The bug may cause an oops looking something like: + + BUG: KASAN: slab-out-of-bounds in watch_queue_set_filter+0x659/0x740 + Write of size 4 at addr ffff88800d2c66bc by task watch_queue_oob/611 + ... + Call Trace: + + dump_stack_lvl+0x45/0x59 + print_address_description.constprop.0+0x1f/0x150 + ... + kasan_report.cold+0x7f/0x11b + ... + watch_queue_set_filter+0x659/0x740 + ... + __x64_sys_ioctl+0x127/0x190 + do_syscall_64+0x43/0x90 + entry_SYSCALL_64_after_hwframe+0x44/0xae + + Allocated by task 611: + kasan_save_stack+0x1e/0x40 + __kasan_kmalloc+0x81/0xa0 + watch_queue_set_filter+0x23a/0x740 + __x64_sys_ioctl+0x127/0x190 + do_syscall_64+0x43/0x90 + entry_SYSCALL_64_after_hwframe+0x44/0xae + + The buggy address belongs to the object at ffff88800d2c66a0 + which belongs to the cache kmalloc-32 of size 32 + The buggy address is located 28 bytes inside of + 32-byte region [ffff88800d2c66a0, ffff88800d2c66c0) + +Fixes: c73be61cede5 ("pipe: Add general notification queue support") +Reported-by: Jann Horn +Signed-off-by: David Howells +Signed-off-by: Linus Torvalds +--- + include/linux/watch_queue.h | 3 ++- + kernel/watch_queue.c | 4 ++-- + 2 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/include/linux/watch_queue.h b/include/linux/watch_queue.h +index c994d1b2cdbaa..3b9a40ae8bdba 100644 +--- a/include/linux/watch_queue.h ++++ b/include/linux/watch_queue.h +@@ -28,7 +28,8 @@ struct watch_type_filter { + struct watch_filter { + union { + struct rcu_head rcu; +- unsigned long type_filter[2]; /* Bitmask of accepted types */ ++ /* Bitmask of accepted types */ ++ DECLARE_BITMAP(type_filter, WATCH_TYPE__NR); + }; + u32 nr_filters; /* Number of filters */ + struct watch_type_filter filters[]; +diff --git a/kernel/watch_queue.c b/kernel/watch_queue.c +index 9c9eb20dd2c50..427b0318e303c 100644 +--- a/kernel/watch_queue.c ++++ b/kernel/watch_queue.c +@@ -320,7 +320,7 @@ long watch_queue_set_filter(struct pipe_inode_info *pipe, + tf[i].info_mask & WATCH_INFO_LENGTH) + goto err_filter; + /* Ignore any unknown types */ +- if (tf[i].type >= sizeof(wfilter->type_filter) * 8) ++ if (tf[i].type >= WATCH_TYPE__NR) + continue; + nr_filter++; + } +@@ -336,7 +336,7 @@ long watch_queue_set_filter(struct pipe_inode_info *pipe, + + q = wfilter->filters; + for (i = 0; i < filter.nr_filters; i++) { +- if (tf[i].type >= sizeof(wfilter->type_filter) * BITS_PER_LONG) ++ if (tf[i].type >= WATCH_TYPE__NR) + continue; + + q->type = tf[i].type; +-- +cgit diff --git a/tests/cases/CVE-2022-1011.patch b/tests/cases/CVE-2022-1011.patch new file mode 100644 index 0000000..f24dc03 --- /dev/null +++ b/tests/cases/CVE-2022-1011.patch @@ -0,0 +1,82 @@ +From 0c4bcfdecb1ac0967619ee7ff44871d93c08c909 Mon Sep 17 00:00:00 2001 +From: Miklos Szeredi +Date: Mon, 7 Mar 2022 16:30:44 +0100 +Subject: fuse: fix pipe buffer lifetime for direct_io + +In FOPEN_DIRECT_IO mode, fuse_file_write_iter() calls +fuse_direct_write_iter(), which normally calls fuse_direct_io(), which then +imports the write buffer with fuse_get_user_pages(), which uses +iov_iter_get_pages() to grab references to userspace pages instead of +actually copying memory. + +On the filesystem device side, these pages can then either be read to +userspace (via fuse_dev_read()), or splice()d over into a pipe using +fuse_dev_splice_read() as pipe buffers with &nosteal_pipe_buf_ops. + +This is wrong because after fuse_dev_do_read() unlocks the FUSE request, +the userspace filesystem can mark the request as completed, causing write() +to return. At that point, the userspace filesystem should no longer have +access to the pipe buffer. + +Fix by copying pages coming from the user address space to new pipe +buffers. + +Reported-by: Jann Horn +Fixes: c3021629a0d8 ("fuse: support splice() reading from fuse device") +Cc: +Signed-off-by: Miklos Szeredi +--- + fs/fuse/dev.c | 12 +++++++++++- + fs/fuse/file.c | 1 + + fs/fuse/fuse_i.h | 1 + + 3 files changed, 13 insertions(+), 1 deletion(-) + +diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c +index cd54a529460da..592730fd6e424 100644 +--- a/fs/fuse/dev.c ++++ b/fs/fuse/dev.c +@@ -941,7 +941,17 @@ static int fuse_copy_page(struct fuse_copy_state *cs, struct page **pagep, + + while (count) { + if (cs->write && cs->pipebufs && page) { +- return fuse_ref_page(cs, page, offset, count); ++ /* ++ * Can't control lifetime of pipe buffers, so always ++ * copy user pages. ++ */ ++ if (cs->req->args->user_pages) { ++ err = fuse_copy_fill(cs); ++ if (err) ++ return err; ++ } else { ++ return fuse_ref_page(cs, page, offset, count); ++ } + } else if (!cs->len) { + if (cs->move_pages && page && + offset == 0 && count == PAGE_SIZE) { +diff --git a/fs/fuse/file.c b/fs/fuse/file.c +index 8290944517749..0fc150c1c50bd 100644 +--- a/fs/fuse/file.c ++++ b/fs/fuse/file.c +@@ -1413,6 +1413,7 @@ static int fuse_get_user_pages(struct fuse_args_pages *ap, struct iov_iter *ii, + (PAGE_SIZE - ret) & (PAGE_SIZE - 1); + } + ++ ap->args.user_pages = true; + if (write) + ap->args.in_pages = true; + else +diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h +index e8e59fbdefebe..eac4984cc753c 100644 +--- a/fs/fuse/fuse_i.h ++++ b/fs/fuse/fuse_i.h +@@ -256,6 +256,7 @@ struct fuse_args { + bool nocreds:1; + bool in_pages:1; + bool out_pages:1; ++ bool user_pages:1; + bool out_argvar:1; + bool page_zeroing:1; + bool page_replace:1; +-- +cgit diff --git a/tests/cases/CVE-2022-1015.patch b/tests/cases/CVE-2022-1015.patch new file mode 100644 index 0000000..3e35c82 --- /dev/null +++ b/tests/cases/CVE-2022-1015.patch @@ -0,0 +1,70 @@ +From 6e1acfa387b9ff82cfc7db8cc3b6959221a95851 Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Thu, 17 Mar 2022 11:59:26 +0100 +Subject: netfilter: nf_tables: validate registers coming from userspace. + +Bail out in case userspace uses unsupported registers. + +Fixes: 49499c3e6e18 ("netfilter: nf_tables: switch registers to 32 bit addressing") +Signed-off-by: Pablo Neira Ayuso +--- + net/netfilter/nf_tables_api.c | 22 +++++++++++++++++----- + 1 file changed, 17 insertions(+), 5 deletions(-) + +diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c +index d71a33ae39b35..1f5a0eece0d14 100644 +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -9275,17 +9275,23 @@ int nft_parse_u32_check(const struct nlattr *attr, int max, u32 *dest) + } + EXPORT_SYMBOL_GPL(nft_parse_u32_check); + +-static unsigned int nft_parse_register(const struct nlattr *attr) ++static unsigned int nft_parse_register(const struct nlattr *attr, u32 *preg) + { + unsigned int reg; + + reg = ntohl(nla_get_be32(attr)); + switch (reg) { + case NFT_REG_VERDICT...NFT_REG_4: +- return reg * NFT_REG_SIZE / NFT_REG32_SIZE; ++ *preg = reg * NFT_REG_SIZE / NFT_REG32_SIZE; ++ break; ++ case NFT_REG32_00...NFT_REG32_15: ++ *preg = reg + NFT_REG_SIZE / NFT_REG32_SIZE - NFT_REG32_00; ++ break; + default: +- return reg + NFT_REG_SIZE / NFT_REG32_SIZE - NFT_REG32_00; ++ return -ERANGE; + } ++ ++ return 0; + } + + /** +@@ -9327,7 +9333,10 @@ int nft_parse_register_load(const struct nlattr *attr, u8 *sreg, u32 len) + u32 reg; + int err; + +- reg = nft_parse_register(attr); ++ err = nft_parse_register(attr, ®); ++ if (err < 0) ++ return err; ++ + err = nft_validate_register_load(reg, len); + if (err < 0) + return err; +@@ -9382,7 +9391,10 @@ int nft_parse_register_store(const struct nft_ctx *ctx, + int err; + u32 reg; + +- reg = nft_parse_register(attr); ++ err = nft_parse_register(attr, ®); ++ if (err < 0) ++ return err; ++ + err = nft_validate_register_store(ctx, reg, data, type, len); + if (err < 0) + return err; +-- +cgit diff --git a/tests/cases/CVE-2022-1679.patch b/tests/cases/CVE-2022-1679.patch new file mode 100644 index 0000000..516aa4c --- /dev/null +++ b/tests/cases/CVE-2022-1679.patch @@ -0,0 +1,88 @@ +From 0ac4827f78c7ffe8eef074bc010e7e34bc22f533 Mon Sep 17 00:00:00 2001 +From: Pavel Skripkin +Date: Mon, 13 Jun 2022 21:43:59 +0300 +Subject: ath9k: fix use-after-free in ath9k_hif_usb_rx_cb +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Syzbot reported use-after-free Read in ath9k_hif_usb_rx_cb() [0]. The +problem was in incorrect htc_handle->drv_priv initialization. + +Probable call trace which can trigger use-after-free: + +ath9k_htc_probe_device() + /* htc_handle->drv_priv = priv; */ + ath9k_htc_wait_for_target() <--- Failed + ieee80211_free_hw() <--- priv pointer is freed + + +... +ath9k_hif_usb_rx_cb() + ath9k_hif_usb_rx_stream() + RX_STAT_INC() <--- htc_handle->drv_priv access + +In order to not add fancy protection for drv_priv we can move +htc_handle->drv_priv initialization at the end of the +ath9k_htc_probe_device() and add helper macro to make +all *_STAT_* macros NULL safe, since syzbot has reported related NULL +deref in that macros [1] + +Link: https://syzkaller.appspot.com/bug?id=6ead44e37afb6866ac0c7dd121b4ce07cb665f60 [0] +Link: https://syzkaller.appspot.com/bug?id=b8101ffcec107c0567a0cd8acbbacec91e9ee8de [1] +Fixes: fb9987d0f748 ("ath9k_htc: Support for AR9271 chipset.") +Reported-and-tested-by: syzbot+03110230a11411024147@syzkaller.appspotmail.com +Reported-and-tested-by: syzbot+c6dde1f690b60e0b9fbe@syzkaller.appspotmail.com +Signed-off-by: Pavel Skripkin +Acked-by: Toke Høiland-Jørgensen +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/d57bbedc857950659bfacac0ab48790c1eda00c8.1655145743.git.paskripkin@gmail.com +--- + drivers/net/wireless/ath/ath9k/htc.h | 10 +++++----- + drivers/net/wireless/ath/ath9k/htc_drv_init.c | 3 ++- + 2 files changed, 7 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h +index 6b45e63fae4ba..e3d546ef71ddc 100644 +--- a/drivers/net/wireless/ath/ath9k/htc.h ++++ b/drivers/net/wireless/ath/ath9k/htc.h +@@ -327,11 +327,11 @@ static inline struct ath9k_htc_tx_ctl *HTC_SKB_CB(struct sk_buff *skb) + } + + #ifdef CONFIG_ATH9K_HTC_DEBUGFS +- +-#define TX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.tx_stats.c++) +-#define TX_STAT_ADD(c, a) (hif_dev->htc_handle->drv_priv->debug.tx_stats.c += a) +-#define RX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.skbrx_stats.c++) +-#define RX_STAT_ADD(c, a) (hif_dev->htc_handle->drv_priv->debug.skbrx_stats.c += a) ++#define __STAT_SAFE(expr) (hif_dev->htc_handle->drv_priv ? (expr) : 0) ++#define TX_STAT_INC(c) __STAT_SAFE(hif_dev->htc_handle->drv_priv->debug.tx_stats.c++) ++#define TX_STAT_ADD(c, a) __STAT_SAFE(hif_dev->htc_handle->drv_priv->debug.tx_stats.c += a) ++#define RX_STAT_INC(c) __STAT_SAFE(hif_dev->htc_handle->drv_priv->debug.skbrx_stats.c++) ++#define RX_STAT_ADD(c, a) __STAT_SAFE(hif_dev->htc_handle->drv_priv->debug.skbrx_stats.c += a) + #define CAB_STAT_INC priv->debug.tx_stats.cab_queued++ + + #define TX_QSTAT_INC(q) (priv->debug.tx_stats.queue_stats[q]++) +diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c +index ff61ae34ecdf0..07ac88fb1c577 100644 +--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c ++++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c +@@ -944,7 +944,6 @@ int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev, + priv->hw = hw; + priv->htc = htc_handle; + priv->dev = dev; +- htc_handle->drv_priv = priv; + SET_IEEE80211_DEV(hw, priv->dev); + + ret = ath9k_htc_wait_for_target(priv); +@@ -965,6 +964,8 @@ int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev, + if (ret) + goto err_init; + ++ htc_handle->drv_priv = priv; ++ + return 0; + + err_init: +-- +cgit diff --git a/tests/cases/CVE-2022-24122.patch b/tests/cases/CVE-2022-24122.patch new file mode 100644 index 0000000..3d1636f --- /dev/null +++ b/tests/cases/CVE-2022-24122.patch @@ -0,0 +1,56 @@ +From f9d87929d451d3e649699d0f1d74f71f77ad38f5 Mon Sep 17 00:00:00 2001 +From: "Eric W. Biederman" +Date: Mon, 24 Jan 2022 12:46:50 -0600 +Subject: ucount: Make get_ucount a safe get_user replacement + +When the ucount code was refactored to create get_ucount it was missed +that some of the contexts in which a rlimit is kept elevated can be +the only reference to the user/ucount in the system. + +Ordinary ucount references exist in places that also have a reference +to the user namspace, but in POSIX message queues, the SysV shm code, +and the SIGPENDING code there is no independent user namespace +reference. + +Inspection of the the user_namespace show no instance of circular +references between struct ucounts and the user_namespace. So +hold a reference from struct ucount to i's user_namespace to +resolve this problem. + +Link: https://lore.kernel.org/lkml/YZV7Z+yXbsx9p3JN@fixkernel.com/ +Reported-by: Qian Cai +Reported-by: Mathias Krause +Tested-by: Mathias Krause +Reviewed-by: Mathias Krause +Reviewed-by: Alexey Gladkov +Fixes: d64696905554 ("Reimplement RLIMIT_SIGPENDING on top of ucounts") +Fixes: 6e52a9f0532f ("Reimplement RLIMIT_MSGQUEUE on top of ucounts") +Fixes: d7c9e99aee48 ("Reimplement RLIMIT_MEMLOCK on top of ucounts") +Cc: stable@vger.kernel.org +Signed-off-by: "Eric W. Biederman" +--- + kernel/ucount.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/kernel/ucount.c b/kernel/ucount.c +index 7b32c356ebc5c..65b597431c861 100644 +--- a/kernel/ucount.c ++++ b/kernel/ucount.c +@@ -190,6 +190,7 @@ struct ucounts *alloc_ucounts(struct user_namespace *ns, kuid_t uid) + kfree(new); + } else { + hlist_add_head(&new->node, hashent); ++ get_user_ns(new->ns); + spin_unlock_irq(&ucounts_lock); + return new; + } +@@ -210,6 +211,7 @@ void put_ucounts(struct ucounts *ucounts) + if (atomic_dec_and_lock_irqsave(&ucounts->count, &ucounts_lock, flags)) { + hlist_del_init(&ucounts->node); + spin_unlock_irqrestore(&ucounts_lock, flags); ++ put_user_ns(ucounts->ns); + kfree(ucounts); + } + } +-- +cgit diff --git a/tests/cases/CVE-2022-25636.patch b/tests/cases/CVE-2022-25636.patch new file mode 100644 index 0000000..d2531af --- /dev/null +++ b/tests/cases/CVE-2022-25636.patch @@ -0,0 +1,148 @@ +From b1a5983f56e371046dcf164f90bfaf704d2b89f6 Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Thu, 17 Feb 2022 23:41:20 +0100 +Subject: netfilter: nf_tables_offload: incorrect flow offload action array + size + +immediate verdict expression needs to allocate one slot in the flow offload +action array, however, immediate data expression does not need to do so. + +fwd and dup expression need to allocate one slot, this is missing. + +Add a new offload_action interface to report if this expression needs to +allocate one slot in the flow offload action array. + +Fixes: be2861dc36d7 ("netfilter: nft_{fwd,dup}_netdev: add offload support") +Reported-and-tested-by: Nick Gregory +Signed-off-by: Pablo Neira Ayuso +--- + include/net/netfilter/nf_tables.h | 2 +- + include/net/netfilter/nf_tables_offload.h | 2 -- + net/netfilter/nf_tables_offload.c | 3 ++- + net/netfilter/nft_dup_netdev.c | 6 ++++++ + net/netfilter/nft_fwd_netdev.c | 6 ++++++ + net/netfilter/nft_immediate.c | 12 +++++++++++- + 6 files changed, 26 insertions(+), 5 deletions(-) + +diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h +index eaf55da9a2050..c4c0861deac12 100644 +--- a/include/net/netfilter/nf_tables.h ++++ b/include/net/netfilter/nf_tables.h +@@ -905,9 +905,9 @@ struct nft_expr_ops { + int (*offload)(struct nft_offload_ctx *ctx, + struct nft_flow_rule *flow, + const struct nft_expr *expr); ++ bool (*offload_action)(const struct nft_expr *expr); + void (*offload_stats)(struct nft_expr *expr, + const struct flow_stats *stats); +- u32 offload_flags; + const struct nft_expr_type *type; + void *data; + }; +diff --git a/include/net/netfilter/nf_tables_offload.h b/include/net/netfilter/nf_tables_offload.h +index f9d95ff82df83..7971478439580 100644 +--- a/include/net/netfilter/nf_tables_offload.h ++++ b/include/net/netfilter/nf_tables_offload.h +@@ -67,8 +67,6 @@ struct nft_flow_rule { + struct flow_rule *rule; + }; + +-#define NFT_OFFLOAD_F_ACTION (1 << 0) +- + void nft_flow_rule_set_addr_type(struct nft_flow_rule *flow, + enum flow_dissector_key_id addr_type); + +diff --git a/net/netfilter/nf_tables_offload.c b/net/netfilter/nf_tables_offload.c +index 9656c16462222..2d36952b13920 100644 +--- a/net/netfilter/nf_tables_offload.c ++++ b/net/netfilter/nf_tables_offload.c +@@ -94,7 +94,8 @@ struct nft_flow_rule *nft_flow_rule_create(struct net *net, + + expr = nft_expr_first(rule); + while (nft_expr_more(rule, expr)) { +- if (expr->ops->offload_flags & NFT_OFFLOAD_F_ACTION) ++ if (expr->ops->offload_action && ++ expr->ops->offload_action(expr)) + num_actions++; + + expr = nft_expr_next(expr); +diff --git a/net/netfilter/nft_dup_netdev.c b/net/netfilter/nft_dup_netdev.c +index bbf3fcba3df40..5b5c607fbf83f 100644 +--- a/net/netfilter/nft_dup_netdev.c ++++ b/net/netfilter/nft_dup_netdev.c +@@ -67,6 +67,11 @@ static int nft_dup_netdev_offload(struct nft_offload_ctx *ctx, + return nft_fwd_dup_netdev_offload(ctx, flow, FLOW_ACTION_MIRRED, oif); + } + ++static bool nft_dup_netdev_offload_action(const struct nft_expr *expr) ++{ ++ return true; ++} ++ + static struct nft_expr_type nft_dup_netdev_type; + static const struct nft_expr_ops nft_dup_netdev_ops = { + .type = &nft_dup_netdev_type, +@@ -75,6 +80,7 @@ static const struct nft_expr_ops nft_dup_netdev_ops = { + .init = nft_dup_netdev_init, + .dump = nft_dup_netdev_dump, + .offload = nft_dup_netdev_offload, ++ .offload_action = nft_dup_netdev_offload_action, + }; + + static struct nft_expr_type nft_dup_netdev_type __read_mostly = { +diff --git a/net/netfilter/nft_fwd_netdev.c b/net/netfilter/nft_fwd_netdev.c +index fa9301ca60331..619e394a91de9 100644 +--- a/net/netfilter/nft_fwd_netdev.c ++++ b/net/netfilter/nft_fwd_netdev.c +@@ -79,6 +79,11 @@ static int nft_fwd_netdev_offload(struct nft_offload_ctx *ctx, + return nft_fwd_dup_netdev_offload(ctx, flow, FLOW_ACTION_REDIRECT, oif); + } + ++static bool nft_fwd_netdev_offload_action(const struct nft_expr *expr) ++{ ++ return true; ++} ++ + struct nft_fwd_neigh { + u8 sreg_dev; + u8 sreg_addr; +@@ -222,6 +227,7 @@ static const struct nft_expr_ops nft_fwd_netdev_ops = { + .dump = nft_fwd_netdev_dump, + .validate = nft_fwd_validate, + .offload = nft_fwd_netdev_offload, ++ .offload_action = nft_fwd_netdev_offload_action, + }; + + static const struct nft_expr_ops * +diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c +index 90c64d27ae532..d0f67d325bdfd 100644 +--- a/net/netfilter/nft_immediate.c ++++ b/net/netfilter/nft_immediate.c +@@ -213,6 +213,16 @@ static int nft_immediate_offload(struct nft_offload_ctx *ctx, + return 0; + } + ++static bool nft_immediate_offload_action(const struct nft_expr *expr) ++{ ++ const struct nft_immediate_expr *priv = nft_expr_priv(expr); ++ ++ if (priv->dreg == NFT_REG_VERDICT) ++ return true; ++ ++ return false; ++} ++ + static const struct nft_expr_ops nft_imm_ops = { + .type = &nft_imm_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_immediate_expr)), +@@ -224,7 +234,7 @@ static const struct nft_expr_ops nft_imm_ops = { + .dump = nft_immediate_dump, + .validate = nft_immediate_validate, + .offload = nft_immediate_offload, +- .offload_flags = NFT_OFFLOAD_F_ACTION, ++ .offload_action = nft_immediate_offload_action, + }; + + struct nft_expr_type nft_imm_type __read_mostly = { +-- +cgit diff --git a/tests/cases/CVE-2022-2585.patch b/tests/cases/CVE-2022-2585.patch new file mode 100644 index 0000000..b36904b --- /dev/null +++ b/tests/cases/CVE-2022-2585.patch @@ -0,0 +1,46 @@ +From e362359ace6f87c201531872486ff295df306d13 Mon Sep 17 00:00:00 2001 +From: Thadeu Lima de Souza Cascardo +Date: Tue, 9 Aug 2022 14:07:51 -0300 +Subject: posix-cpu-timers: Cleanup CPU timers before freeing them during exec + +Commit 55e8c8eb2c7b ("posix-cpu-timers: Store a reference to a pid not a +task") started looking up tasks by PID when deleting a CPU timer. + +When a non-leader thread calls execve, it will switch PIDs with the leader +process. Then, as it calls exit_itimers, posix_cpu_timer_del cannot find +the task because the timer still points out to the old PID. + +That means that armed timers won't be disarmed, that is, they won't be +removed from the timerqueue_list. exit_itimers will still release their +memory, and when that list is later processed, it leads to a +use-after-free. + +Clean up the timers from the de-threaded task before freeing them. This +prevents a reported use-after-free. + +Fixes: 55e8c8eb2c7b ("posix-cpu-timers: Store a reference to a pid not a task") +Signed-off-by: Thadeu Lima de Souza Cascardo +Signed-off-by: Thomas Gleixner +Reviewed-by: Thomas Gleixner +Cc: +Link: https://lore.kernel.org/r/20220809170751.164716-1-cascardo@canonical.com +--- + fs/exec.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/fs/exec.c b/fs/exec.c +index 5fd73915c62ce..f793221f4eb63 100644 +--- a/fs/exec.c ++++ b/fs/exec.c +@@ -1304,6 +1304,9 @@ int begin_new_exec(struct linux_binprm * bprm) + bprm->mm = NULL; + + #ifdef CONFIG_POSIX_TIMERS ++ spin_lock_irq(&me->sighand->siglock); ++ posix_cpu_timers_exit(me); ++ spin_unlock_irq(&me->sighand->siglock); + exit_itimers(me); + flush_itimer_signals(); + #endif +-- +cgit diff --git a/tests/cases/CVE-2022-2602.patch b/tests/cases/CVE-2022-2602.patch new file mode 100644 index 0000000..5ef7815 --- /dev/null +++ b/tests/cases/CVE-2022-2602.patch @@ -0,0 +1,102 @@ +From 0091bfc81741b8d3aeb3b7ab8636f911b2de6e80 Mon Sep 17 00:00:00 2001 +From: Pavel Begunkov +Date: Mon, 3 Oct 2022 13:59:47 +0100 +Subject: io_uring/af_unix: defer registered files gc to io_uring release + +Instead of putting io_uring's registered files in unix_gc() we want it +to be done by io_uring itself. The trick here is to consider io_uring +registered files for cycle detection but not actually putting them down. +Because io_uring can't register other ring instances, this will remove +all refs to the ring file triggering the ->release path and clean up +with io_ring_ctx_free(). + +Cc: stable@vger.kernel.org +Fixes: 6b06314c47e1 ("io_uring: add file set registration") +Reported-and-tested-by: David Bouman +Signed-off-by: Pavel Begunkov +Signed-off-by: Thadeu Lima de Souza Cascardo +[axboe: add kerneldoc comment to skb, fold in skb leak fix] +Signed-off-by: Jens Axboe +--- + include/linux/skbuff.h | 2 ++ + io_uring/rsrc.c | 1 + + net/unix/garbage.c | 20 ++++++++++++++++++++ + 3 files changed, 23 insertions(+) + +diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h +index 9fcf534f2d927..7be5bb4c94b6d 100644 +--- a/include/linux/skbuff.h ++++ b/include/linux/skbuff.h +@@ -803,6 +803,7 @@ typedef unsigned char *sk_buff_data_t; + * @csum_level: indicates the number of consecutive checksums found in + * the packet minus one that have been verified as + * CHECKSUM_UNNECESSARY (max 3) ++ * @scm_io_uring: SKB holds io_uring registered files + * @dst_pending_confirm: need to confirm neighbour + * @decrypted: Decrypted SKB + * @slow_gro: state present at GRO time, slower prepare step required +@@ -982,6 +983,7 @@ struct sk_buff { + #endif + __u8 slow_gro:1; + __u8 csum_not_inet:1; ++ __u8 scm_io_uring:1; + + #ifdef CONFIG_NET_SCHED + __u16 tc_index; /* traffic control index */ +diff --git a/io_uring/rsrc.c b/io_uring/rsrc.c +index 6f88ded0e7e56..012fdb04ec238 100644 +--- a/io_uring/rsrc.c ++++ b/io_uring/rsrc.c +@@ -855,6 +855,7 @@ int __io_scm_file_account(struct io_ring_ctx *ctx, struct file *file) + + UNIXCB(skb).fp = fpl; + skb->sk = sk; ++ skb->scm_io_uring = 1; + skb->destructor = unix_destruct_scm; + refcount_add(skb->truesize, &sk->sk_wmem_alloc); + } +diff --git a/net/unix/garbage.c b/net/unix/garbage.c +index d45d5366115a7..dc27635403932 100644 +--- a/net/unix/garbage.c ++++ b/net/unix/garbage.c +@@ -204,6 +204,7 @@ void wait_for_unix_gc(void) + /* The external entry point: unix_gc() */ + void unix_gc(void) + { ++ struct sk_buff *next_skb, *skb; + struct unix_sock *u; + struct unix_sock *next; + struct sk_buff_head hitlist; +@@ -297,11 +298,30 @@ void unix_gc(void) + + spin_unlock(&unix_gc_lock); + ++ /* We need io_uring to clean its registered files, ignore all io_uring ++ * originated skbs. It's fine as io_uring doesn't keep references to ++ * other io_uring instances and so killing all other files in the cycle ++ * will put all io_uring references forcing it to go through normal ++ * release.path eventually putting registered files. ++ */ ++ skb_queue_walk_safe(&hitlist, skb, next_skb) { ++ if (skb->scm_io_uring) { ++ __skb_unlink(skb, &hitlist); ++ skb_queue_tail(&skb->sk->sk_receive_queue, skb); ++ } ++ } ++ + /* Here we are. Hitlist is filled. Die. */ + __skb_queue_purge(&hitlist); + + spin_lock(&unix_gc_lock); + ++ /* There could be io_uring registered files, just push them back to ++ * the inflight list ++ */ ++ list_for_each_entry_safe(u, next, &gc_candidates, link) ++ list_move_tail(&u->link, &gc_inflight_list); ++ + /* All candidates should have been detached by now. */ + BUG_ON(!list_empty(&gc_candidates)); + +-- +cgit diff --git a/tests/cases/CVE-2022-2639.patch b/tests/cases/CVE-2022-2639.patch new file mode 100644 index 0000000..8852bf7 --- /dev/null +++ b/tests/cases/CVE-2022-2639.patch @@ -0,0 +1,82 @@ +From cefa91b2332d7009bc0be5d951d6cbbf349f90f8 Mon Sep 17 00:00:00 2001 +From: Paolo Valerio +Date: Fri, 15 Apr 2022 10:08:41 +0200 +Subject: openvswitch: fix OOB access in reserve_sfa_size() + +Given a sufficiently large number of actions, while copying and +reserving memory for a new action of a new flow, if next_offset is +greater than MAX_ACTIONS_BUFSIZE, the function reserve_sfa_size() does +not return -EMSGSIZE as expected, but it allocates MAX_ACTIONS_BUFSIZE +bytes increasing actions_len by req_size. This can then lead to an OOB +write access, especially when further actions need to be copied. + +Fix it by rearranging the flow action size check. + +KASAN splat below: + +================================================================== +BUG: KASAN: slab-out-of-bounds in reserve_sfa_size+0x1ba/0x380 [openvswitch] +Write of size 65360 at addr ffff888147e4001c by task handler15/836 + +CPU: 1 PID: 836 Comm: handler15 Not tainted 5.18.0-rc1+ #27 +... +Call Trace: + + dump_stack_lvl+0x45/0x5a + print_report.cold+0x5e/0x5db + ? __lock_text_start+0x8/0x8 + ? reserve_sfa_size+0x1ba/0x380 [openvswitch] + kasan_report+0xb5/0x130 + ? reserve_sfa_size+0x1ba/0x380 [openvswitch] + kasan_check_range+0xf5/0x1d0 + memcpy+0x39/0x60 + reserve_sfa_size+0x1ba/0x380 [openvswitch] + __add_action+0x24/0x120 [openvswitch] + ovs_nla_add_action+0xe/0x20 [openvswitch] + ovs_ct_copy_action+0x29d/0x1130 [openvswitch] + ? __kernel_text_address+0xe/0x30 + ? unwind_get_return_address+0x56/0xa0 + ? create_prof_cpu_mask+0x20/0x20 + ? ovs_ct_verify+0xf0/0xf0 [openvswitch] + ? prep_compound_page+0x198/0x2a0 + ? __kasan_check_byte+0x10/0x40 + ? kasan_unpoison+0x40/0x70 + ? ksize+0x44/0x60 + ? reserve_sfa_size+0x75/0x380 [openvswitch] + __ovs_nla_copy_actions+0xc26/0x2070 [openvswitch] + ? __zone_watermark_ok+0x420/0x420 + ? validate_set.constprop.0+0xc90/0xc90 [openvswitch] + ? __alloc_pages+0x1a9/0x3e0 + ? __alloc_pages_slowpath.constprop.0+0x1da0/0x1da0 + ? unwind_next_frame+0x991/0x1e40 + ? __mod_node_page_state+0x99/0x120 + ? __mod_lruvec_page_state+0x2e3/0x470 + ? __kasan_kmalloc_large+0x90/0xe0 + ovs_nla_copy_actions+0x1b4/0x2c0 [openvswitch] + ovs_flow_cmd_new+0x3cd/0xb10 [openvswitch] + ... + +Cc: stable@vger.kernel.org +Fixes: f28cd2af22a0 ("openvswitch: fix flow actions reallocation") +Signed-off-by: Paolo Valerio +Acked-by: Eelco Chaudron +Signed-off-by: David S. Miller +--- + net/openvswitch/flow_netlink.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c +index 7176156d38443c..4c09cf8a0ab2dc 100644 +--- a/net/openvswitch/flow_netlink.c ++++ b/net/openvswitch/flow_netlink.c +@@ -2465,7 +2465,7 @@ static struct nlattr *reserve_sfa_size(struct sw_flow_actions **sfa, + new_acts_size = max(next_offset + req_size, ksize(*sfa) * 2); + + if (new_acts_size > MAX_ACTIONS_BUFSIZE) { +- if ((MAX_ACTIONS_BUFSIZE - next_offset) < req_size) { ++ if ((next_offset + req_size) > MAX_ACTIONS_BUFSIZE) { + OVS_NLERR(log, "Flow action size exceeds max %u", + MAX_ACTIONS_BUFSIZE); + return ERR_PTR(-EMSGSIZE); +-- +cgit diff --git a/tests/cases/CVE-2022-27666.patch b/tests/cases/CVE-2022-27666.patch new file mode 100644 index 0000000..151acbf --- /dev/null +++ b/tests/cases/CVE-2022-27666.patch @@ -0,0 +1,86 @@ +From ebe48d368e97d007bfeb76fcb065d6cfc4c96645 Mon Sep 17 00:00:00 2001 +From: Steffen Klassert +Date: Mon, 7 Mar 2022 13:11:39 +0100 +Subject: esp: Fix possible buffer overflow in ESP transformation + +The maximum message size that can be send is bigger than +the maximum site that skb_page_frag_refill can allocate. +So it is possible to write beyond the allocated buffer. + +Fix this by doing a fallback to COW in that case. + +v2: + +Avoid get get_order() costs as suggested by Linus Torvalds. + +Fixes: cac2661c53f3 ("esp4: Avoid skb_cow_data whenever possible") +Fixes: 03e2a30f6a27 ("esp6: Avoid skb_cow_data whenever possible") +Reported-by: valis +Signed-off-by: Steffen Klassert +--- + include/net/esp.h | 2 ++ + net/ipv4/esp4.c | 5 +++++ + net/ipv6/esp6.c | 5 +++++ + 3 files changed, 12 insertions(+) + +diff --git a/include/net/esp.h b/include/net/esp.h +index 9c5637d41d951..90cd02ff77ef6 100644 +--- a/include/net/esp.h ++++ b/include/net/esp.h +@@ -4,6 +4,8 @@ + + #include + ++#define ESP_SKB_FRAG_MAXSIZE (PAGE_SIZE << SKB_FRAG_PAGE_ORDER) ++ + struct ip_esp_hdr; + + static inline struct ip_esp_hdr *ip_esp_hdr(const struct sk_buff *skb) +diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c +index e1b1d080e908d..70e6c87fbe3df 100644 +--- a/net/ipv4/esp4.c ++++ b/net/ipv4/esp4.c +@@ -446,6 +446,7 @@ int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info * + struct page *page; + struct sk_buff *trailer; + int tailen = esp->tailen; ++ unsigned int allocsz; + + /* this is non-NULL only with TCP/UDP Encapsulation */ + if (x->encap) { +@@ -455,6 +456,10 @@ int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info * + return err; + } + ++ allocsz = ALIGN(skb->data_len + tailen, L1_CACHE_BYTES); ++ if (allocsz > ESP_SKB_FRAG_MAXSIZE) ++ goto cow; ++ + if (!skb_cloned(skb)) { + if (tailen <= skb_tailroom(skb)) { + nfrags = 1; +diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c +index 7591160edce14..b0ffbcd5432d6 100644 +--- a/net/ipv6/esp6.c ++++ b/net/ipv6/esp6.c +@@ -482,6 +482,7 @@ int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info + struct page *page; + struct sk_buff *trailer; + int tailen = esp->tailen; ++ unsigned int allocsz; + + if (x->encap) { + int err = esp6_output_encap(x, skb, esp); +@@ -490,6 +491,10 @@ int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info + return err; + } + ++ allocsz = ALIGN(skb->data_len + tailen, L1_CACHE_BYTES); ++ if (allocsz > ESP_SKB_FRAG_MAXSIZE) ++ goto cow; ++ + if (!skb_cloned(skb)) { + if (tailen <= skb_tailroom(skb)) { + nfrags = 1; +-- +cgit diff --git a/tests/cases/CVE-2022-29582.patch b/tests/cases/CVE-2022-29582.patch new file mode 100644 index 0000000..2c39943 --- /dev/null +++ b/tests/cases/CVE-2022-29582.patch @@ -0,0 +1,55 @@ +From e677edbcabee849bfdd43f1602bccbecf736a646 Mon Sep 17 00:00:00 2001 +From: Jens Axboe +Date: Fri, 8 Apr 2022 11:08:58 -0600 +Subject: io_uring: fix race between timeout flush and removal + +io_flush_timeouts() assumes the timeout isn't in progress of triggering +or being removed/canceled, so it unconditionally removes it from the +timeout list and attempts to cancel it. + +Leave it on the list and let the normal timeout cancelation take care +of it. + +Cc: stable@vger.kernel.org # 5.5+ +Signed-off-by: Jens Axboe +--- + fs/io_uring.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/fs/io_uring.c b/fs/io_uring.c +index fafd1ca4780b6..659f8ecba5b79 100644 +--- a/fs/io_uring.c ++++ b/fs/io_uring.c +@@ -1736,12 +1736,11 @@ static __cold void io_flush_timeouts(struct io_ring_ctx *ctx) + __must_hold(&ctx->completion_lock) + { + u32 seq = ctx->cached_cq_tail - atomic_read(&ctx->cq_timeouts); ++ struct io_kiocb *req, *tmp; + + spin_lock_irq(&ctx->timeout_lock); +- while (!list_empty(&ctx->timeout_list)) { ++ list_for_each_entry_safe(req, tmp, &ctx->timeout_list, timeout.list) { + u32 events_needed, events_got; +- struct io_kiocb *req = list_first_entry(&ctx->timeout_list, +- struct io_kiocb, timeout.list); + + if (io_is_timeout_noseq(req)) + break; +@@ -1758,7 +1757,6 @@ static __cold void io_flush_timeouts(struct io_ring_ctx *ctx) + if (events_got < events_needed) + break; + +- list_del_init(&req->timeout.list); + io_kill_timeout(req, 0); + } + ctx->cq_last_tm_flush = seq; +@@ -6628,6 +6626,7 @@ static int io_timeout_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe, + if (data->ts.tv_sec < 0 || data->ts.tv_nsec < 0) + return -EINVAL; + ++ INIT_LIST_HEAD(&req->timeout.list); + data->mode = io_translate_timeout_mode(flags); + hrtimer_init(&data->timer, io_timeout_get_clock(data), data->mode); + +-- +cgit diff --git a/tests/cases/CVE-2022-29968.patch b/tests/cases/CVE-2022-29968.patch new file mode 100644 index 0000000..a6164cb --- /dev/null +++ b/tests/cases/CVE-2022-29968.patch @@ -0,0 +1,29 @@ +From 32452a3eb8b64e01e2be717f518c0be046975b9d Mon Sep 17 00:00:00 2001 +From: Joseph Ravichandran +Date: Thu, 28 Apr 2022 12:57:52 -0400 +Subject: io_uring: fix uninitialized field in rw io_kiocb + +io_rw_init_file does not initialize kiocb->private, so when iocb_bio_iopoll +reads kiocb->private it can contain uninitialized data. + +Fixes: 3e08773c3841 ("block: switch polling to be bio based") +Signed-off-by: Joseph Ravichandran +Signed-off-by: Jens Axboe +--- + fs/io_uring.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/fs/io_uring.c b/fs/io_uring.c +index 92ac50f139cde..e3ae26ff5d1ae 100644 +--- a/fs/io_uring.c ++++ b/fs/io_uring.c +@@ -3783,6 +3783,7 @@ static int io_rw_init_file(struct io_kiocb *req, fmode_t mode) + if (!(kiocb->ki_flags & IOCB_DIRECT) || !file->f_op->iopoll) + return -EOPNOTSUPP; + ++ kiocb->private = NULL; + kiocb->ki_flags |= IOCB_HIPRI | IOCB_ALLOC_CACHE; + kiocb->ki_complete = io_complete_rw_iopoll; + req->iopoll_completed = 0; +-- +cgit diff --git a/tests/cases/CVE-2022-32250.patch b/tests/cases/CVE-2022-32250.patch new file mode 100644 index 0000000..2f87b6a --- /dev/null +++ b/tests/cases/CVE-2022-32250.patch @@ -0,0 +1,98 @@ +From 520778042ccca019f3ffa136dd0ca565c486cedd Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Wed, 25 May 2022 10:36:38 +0200 +Subject: netfilter: nf_tables: disallow non-stateful expression in sets + earlier + +Since 3e135cd499bf ("netfilter: nft_dynset: dynamic stateful expression +instantiation"), it is possible to attach stateful expressions to set +elements. + +cd5125d8f518 ("netfilter: nf_tables: split set destruction in deactivate +and destroy phase") introduces conditional destruction on the object to +accomodate transaction semantics. + +nft_expr_init() calls expr->ops->init() first, then check for +NFT_STATEFUL_EXPR, this stills allows to initialize a non-stateful +lookup expressions which points to a set, which might lead to UAF since +the set is not properly detached from the set->binding for this case. +Anyway, this combination is non-sense from nf_tables perspective. + +This patch fixes this problem by checking for NFT_STATEFUL_EXPR before +expr->ops->init() is called. + +The reporter provides a KASAN splat and a poc reproducer (similar to +those autogenerated by syzbot to report use-after-free errors). It is +unknown to me if they are using syzbot or if they use similar automated +tool to locate the bug that they are reporting. + +For the record, this is the KASAN splat. + +[ 85.431824] ================================================================== +[ 85.432901] BUG: KASAN: use-after-free in nf_tables_bind_set+0x81b/0xa20 +[ 85.433825] Write of size 8 at addr ffff8880286f0e98 by task poc/776 +[ 85.434756] +[ 85.434999] CPU: 1 PID: 776 Comm: poc Tainted: G W 5.18.0+ #2 +[ 85.436023] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.14.0-2 04/01/2014 + +Fixes: 0b2d8a7b638b ("netfilter: nf_tables: add helper functions for expression handling") +Reported-and-tested-by: Aaron Adams +Signed-off-by: Pablo Neira Ayuso +--- + net/netfilter/nf_tables_api.c | 19 ++++++++++--------- + 1 file changed, 10 insertions(+), 9 deletions(-) + +diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c +index 12fc9cda4a2cf..f296dfe86b622 100644 +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -2873,27 +2873,31 @@ static struct nft_expr *nft_expr_init(const struct nft_ctx *ctx, + + err = nf_tables_expr_parse(ctx, nla, &expr_info); + if (err < 0) +- goto err1; ++ goto err_expr_parse; ++ ++ err = -EOPNOTSUPP; ++ if (!(expr_info.ops->type->flags & NFT_EXPR_STATEFUL)) ++ goto err_expr_stateful; + + err = -ENOMEM; + expr = kzalloc(expr_info.ops->size, GFP_KERNEL_ACCOUNT); + if (expr == NULL) +- goto err2; ++ goto err_expr_stateful; + + err = nf_tables_newexpr(ctx, &expr_info, expr); + if (err < 0) +- goto err3; ++ goto err_expr_new; + + return expr; +-err3: ++err_expr_new: + kfree(expr); +-err2: ++err_expr_stateful: + owner = expr_info.ops->type->owner; + if (expr_info.ops->type->release_ops) + expr_info.ops->type->release_ops(expr_info.ops); + + module_put(owner); +-err1: ++err_expr_parse: + return ERR_PTR(err); + } + +@@ -5413,9 +5417,6 @@ struct nft_expr *nft_set_elem_expr_alloc(const struct nft_ctx *ctx, + return expr; + + err = -EOPNOTSUPP; +- if (!(expr->ops->type->flags & NFT_EXPR_STATEFUL)) +- goto err_set_elem_expr; +- + if (expr->ops->type->flags & NFT_EXPR_GC) { + if (set->flags & NFT_SET_TIMEOUT) + goto err_set_elem_expr; +-- +cgit diff --git a/tests/cases/CVE-2022-34918.patch b/tests/cases/CVE-2022-34918.patch new file mode 100644 index 0000000..bd897b0 --- /dev/null +++ b/tests/cases/CVE-2022-34918.patch @@ -0,0 +1,43 @@ +From 7e6bc1f6cabcd30aba0b11219d8e01b952eacbb6 Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Sat, 2 Jul 2022 04:16:30 +0200 +Subject: netfilter: nf_tables: stricter validation of element data + +Make sure element data type and length do not mismatch the one specified +by the set declaration. + +Fixes: 7d7402642eaf ("netfilter: nf_tables: variable sized set element keys / data") +Reported-by: Hugues ANGUELKOV +Signed-off-by: Pablo Neira Ayuso +--- + net/netfilter/nf_tables_api.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c +index 51144fc66889b..d6b59beab3a98 100644 +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -5213,13 +5213,20 @@ static int nft_setelem_parse_data(struct nft_ctx *ctx, struct nft_set *set, + struct nft_data *data, + struct nlattr *attr) + { ++ u32 dtype; + int err; + + err = nft_data_init(ctx, data, NFT_DATA_VALUE_MAXLEN, desc, attr); + if (err < 0) + return err; + +- if (desc->type != NFT_DATA_VERDICT && desc->len != set->dlen) { ++ if (set->dtype == NFT_DATA_VERDICT) ++ dtype = NFT_DATA_VERDICT; ++ else ++ dtype = NFT_DATA_VALUE; ++ ++ if (dtype != desc->type || ++ set->dlen != desc->len) { + nft_data_release(data, desc->type); + return -EINVAL; + } +-- +cgit diff --git a/tests/cases/CVE-2022-3640.patch b/tests/cases/CVE-2022-3640.patch new file mode 100644 index 0000000..cd820c5 --- /dev/null +++ b/tests/cases/CVE-2022-3640.patch @@ -0,0 +1,135 @@ +From 42cf46dea905a80f6de218e837ba4d4cc33d6979 Mon Sep 17 00:00:00 2001 +From: Zhengchao Shao +Date: Mon, 17 Oct 2022 15:58:13 +0800 +Subject: Bluetooth: L2CAP: fix use-after-free in l2cap_conn_del() + +When l2cap_recv_frame() is invoked to receive data, and the cid is +L2CAP_CID_A2MP, if the channel does not exist, it will create a channel. +However, after a channel is created, the hold operation of the channel +is not performed. In this case, the value of channel reference counting +is 1. As a result, after hci_error_reset() is triggered, l2cap_conn_del() +invokes the close hook function of A2MP to release the channel. Then + l2cap_chan_unlock(chan) will trigger UAF issue. + +The process is as follows: +Receive data: +l2cap_data_channel() + a2mp_channel_create() --->channel ref is 2 + l2cap_chan_put() --->channel ref is 1 + +Triger event: + hci_error_reset() + hci_dev_do_close() + ... + l2cap_disconn_cfm() + l2cap_conn_del() + l2cap_chan_hold() --->channel ref is 2 + l2cap_chan_del() --->channel ref is 1 + a2mp_chan_close_cb() --->channel ref is 0, release channel + l2cap_chan_unlock() --->UAF of channel + +The detailed Call Trace is as follows: +BUG: KASAN: use-after-free in __mutex_unlock_slowpath+0xa6/0x5e0 +Read of size 8 at addr ffff8880160664b8 by task kworker/u11:1/7593 +Workqueue: hci0 hci_error_reset +Call Trace: + + dump_stack_lvl+0xcd/0x134 + print_report.cold+0x2ba/0x719 + kasan_report+0xb1/0x1e0 + kasan_check_range+0x140/0x190 + __mutex_unlock_slowpath+0xa6/0x5e0 + l2cap_conn_del+0x404/0x7b0 + l2cap_disconn_cfm+0x8c/0xc0 + hci_conn_hash_flush+0x11f/0x260 + hci_dev_close_sync+0x5f5/0x11f0 + hci_dev_do_close+0x2d/0x70 + hci_error_reset+0x9e/0x140 + process_one_work+0x98a/0x1620 + worker_thread+0x665/0x1080 + kthread+0x2e4/0x3a0 + ret_from_fork+0x1f/0x30 + + +Allocated by task 7593: + kasan_save_stack+0x1e/0x40 + __kasan_kmalloc+0xa9/0xd0 + l2cap_chan_create+0x40/0x930 + amp_mgr_create+0x96/0x990 + a2mp_channel_create+0x7d/0x150 + l2cap_recv_frame+0x51b8/0x9a70 + l2cap_recv_acldata+0xaa3/0xc00 + hci_rx_work+0x702/0x1220 + process_one_work+0x98a/0x1620 + worker_thread+0x665/0x1080 + kthread+0x2e4/0x3a0 + ret_from_fork+0x1f/0x30 + +Freed by task 7593: + kasan_save_stack+0x1e/0x40 + kasan_set_track+0x21/0x30 + kasan_set_free_info+0x20/0x30 + ____kasan_slab_free+0x167/0x1c0 + slab_free_freelist_hook+0x89/0x1c0 + kfree+0xe2/0x580 + l2cap_chan_put+0x22a/0x2d0 + l2cap_conn_del+0x3fc/0x7b0 + l2cap_disconn_cfm+0x8c/0xc0 + hci_conn_hash_flush+0x11f/0x260 + hci_dev_close_sync+0x5f5/0x11f0 + hci_dev_do_close+0x2d/0x70 + hci_error_reset+0x9e/0x140 + process_one_work+0x98a/0x1620 + worker_thread+0x665/0x1080 + kthread+0x2e4/0x3a0 + ret_from_fork+0x1f/0x30 + +Last potentially related work creation: + kasan_save_stack+0x1e/0x40 + __kasan_record_aux_stack+0xbe/0xd0 + call_rcu+0x99/0x740 + netlink_release+0xe6a/0x1cf0 + __sock_release+0xcd/0x280 + sock_close+0x18/0x20 + __fput+0x27c/0xa90 + task_work_run+0xdd/0x1a0 + exit_to_user_mode_prepare+0x23c/0x250 + syscall_exit_to_user_mode+0x19/0x50 + do_syscall_64+0x42/0x80 + entry_SYSCALL_64_after_hwframe+0x63/0xcd + +Second to last potentially related work creation: + kasan_save_stack+0x1e/0x40 + __kasan_record_aux_stack+0xbe/0xd0 + call_rcu+0x99/0x740 + netlink_release+0xe6a/0x1cf0 + __sock_release+0xcd/0x280 + sock_close+0x18/0x20 + __fput+0x27c/0xa90 + task_work_run+0xdd/0x1a0 + exit_to_user_mode_prepare+0x23c/0x250 + syscall_exit_to_user_mode+0x19/0x50 + do_syscall_64+0x42/0x80 + entry_SYSCALL_64_after_hwframe+0x63/0xcd + +Fixes: d0be8347c623 ("Bluetooth: L2CAP: Fix use-after-free caused by l2cap_chan_put") +Signed-off-by: Zhengchao Shao +Signed-off-by: Luiz Augusto von Dentz +--- + net/bluetooth/l2cap_core.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c +index 2283871d3f013..9a32ce6349194 100644 +--- a/net/bluetooth/l2cap_core.c ++++ b/net/bluetooth/l2cap_core.c +@@ -7615,6 +7615,7 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid, + return; + } + ++ l2cap_chan_hold(chan); + l2cap_chan_lock(chan); + } else { + BT_DBG("unknown cid 0x%4.4x", cid); +-- +cgit diff --git a/tests/cases/CVE-2022-36946.patch b/tests/cases/CVE-2022-36946.patch new file mode 100644 index 0000000..ae4b814 --- /dev/null +++ b/tests/cases/CVE-2022-36946.patch @@ -0,0 +1,47 @@ +From 99a63d36cb3ed5ca3aa6fcb64cffbeaf3b0fb164 Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Tue, 26 Jul 2022 12:42:06 +0200 +Subject: netfilter: nf_queue: do not allow packet truncation below transport + header offset + +Domingo Dirutigliano and Nicola Guerrera report kernel panic when +sending nf_queue verdict with 1-byte nfta_payload attribute. + +The IP/IPv6 stack pulls the IP(v6) header from the packet after the +input hook. + +If user truncates the packet below the header size, this skb_pull() will +result in a malformed skb (skb->len < 0). + +Fixes: 7af4cc3fa158 ("[NETFILTER]: Add "nfnetlink_queue" netfilter queue handler over nfnetlink") +Reported-by: Domingo Dirutigliano +Signed-off-by: Florian Westphal +Reviewed-by: Pablo Neira Ayuso +--- + net/netfilter/nfnetlink_queue.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c +index a364f8e5e698f..87a9009d5234d 100644 +--- a/net/netfilter/nfnetlink_queue.c ++++ b/net/netfilter/nfnetlink_queue.c +@@ -843,11 +843,16 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) + } + + static int +-nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e, int diff) ++nfqnl_mangle(void *data, unsigned int data_len, struct nf_queue_entry *e, int diff) + { + struct sk_buff *nskb; + + if (diff < 0) { ++ unsigned int min_len = skb_transport_offset(e->skb); ++ ++ if (data_len < min_len) ++ return -EINVAL; ++ + if (pskb_trim(e->skb, data_len)) + return -ENOMEM; + } else if (diff > 0) { +-- +cgit diff --git a/tests/cases/CVE-2022-3910.patch b/tests/cases/CVE-2022-3910.patch new file mode 100644 index 0000000..c19b7a7 --- /dev/null +++ b/tests/cases/CVE-2022-3910.patch @@ -0,0 +1,32 @@ +From fc7222c3a9f56271fba02aabbfbae999042f1679 Mon Sep 17 00:00:00 2001 +From: Jens Axboe +Date: Thu, 15 Sep 2022 11:44:35 -0600 +Subject: io_uring/msg_ring: check file type before putting + +If we're invoked with a fixed file, follow the normal rules of not +calling io_fput_file(). Fixed files are permanently registered to the +ring, and do not need putting separately. + +Cc: stable@vger.kernel.org +Fixes: aa184e8671f0 ("io_uring: don't attempt to IOPOLL for MSG_RING requests") +Signed-off-by: Jens Axboe +--- + io_uring/msg_ring.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/io_uring/msg_ring.c b/io_uring/msg_ring.c +index 976c4ba68ee7ec..4a7e5d030c782f 100644 +--- a/io_uring/msg_ring.c ++++ b/io_uring/msg_ring.c +@@ -165,7 +165,8 @@ done: + req_set_fail(req); + io_req_set_res(req, ret, 0); + /* put file to avoid an attempt to IOPOLL the req */ +- io_put_file(req->file); ++ if (!(req->flags & REQ_F_FIXED_FILE)) ++ io_put_file(req->file); + req->file = NULL; + return IOU_OK; + } +-- +cgit diff --git a/tests/cases/CVE-2022-41674.patch b/tests/cases/CVE-2022-41674.patch new file mode 100644 index 0000000..87b4a4b --- /dev/null +++ b/tests/cases/CVE-2022-41674.patch @@ -0,0 +1,46 @@ +From aebe9f4639b13a1f4e9a6b42cdd2e38c617b442d Mon Sep 17 00:00:00 2001 +From: Johannes Berg +Date: Wed, 28 Sep 2022 21:56:15 +0200 +Subject: wifi: cfg80211: fix u8 overflow in + cfg80211_update_notlisted_nontrans() + +In the copy code of the elements, we do the following calculation +to reach the end of the MBSSID element: + + /* copy the IEs after MBSSID */ + cpy_len = mbssid[1] + 2; + +This looks fine, however, cpy_len is a u8, the same as mbssid[1], +so the addition of two can overflow. In this case the subsequent +memcpy() will overflow the allocated buffer, since it copies 256 +bytes too much due to the way the allocation and memcpy() sizes +are calculated. + +Fix this by using size_t for the cpy_len variable. + +This fixes CVE-2022-41674. + +Reported-by: Soenke Huster +Tested-by: Soenke Huster +Fixes: 0b8fb8235be8 ("cfg80211: Parsing of Multiple BSSID information in scanning") +Reviewed-by: Kees Cook +Signed-off-by: Johannes Berg +--- + net/wireless/scan.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/wireless/scan.c b/net/wireless/scan.c +index 5382fc2003db4..62f8c10412ad3 100644 +--- a/net/wireless/scan.c ++++ b/net/wireless/scan.c +@@ -2279,7 +2279,7 @@ cfg80211_update_notlisted_nontrans(struct wiphy *wiphy, + size_t new_ie_len; + struct cfg80211_bss_ies *new_ies; + const struct cfg80211_bss_ies *old; +- u8 cpy_len; ++ size_t cpy_len; + + lockdep_assert_held(&wiphy_to_rdev(wiphy)->bss_lock); + +-- +cgit diff --git a/tests/cases/CVE-2022-42719.patch b/tests/cases/CVE-2022-42719.patch new file mode 100644 index 0000000..6fadb88 --- /dev/null +++ b/tests/cases/CVE-2022-42719.patch @@ -0,0 +1,103 @@ +From ff05d4b45dd89b922578dac497dcabf57cf771c6 Mon Sep 17 00:00:00 2001 +From: Johannes Berg +Date: Wed, 28 Sep 2022 22:07:15 +0200 +Subject: wifi: mac80211: fix MBSSID parsing use-after-free + +When we parse a multi-BSSID element, we might point some +element pointers into the allocated nontransmitted_profile. +However, we free this before returning, causing UAF when the +relevant pointers in the parsed elements are accessed. + +Fix this by not allocating the scratch buffer separately but +as part of the returned structure instead, that way, there +are no lifetime issues with it. + +The scratch buffer introduction as part of the returned data +here is taken from MLO feature work done by Ilan. + +This fixes CVE-2022-42719. + +Fixes: 5023b14cf4df ("mac80211: support profile split between elements") +Co-developed-by: Ilan Peer +Signed-off-by: Ilan Peer +Reviewed-by: Kees Cook +Signed-off-by: Johannes Berg +--- + net/mac80211/ieee80211_i.h | 8 ++++++++ + net/mac80211/util.c | 30 +++++++++++++++--------------- + 2 files changed, 23 insertions(+), 15 deletions(-) + +diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h +index 4e1d4c339f2de..a842f2e1c2309 100644 +--- a/net/mac80211/ieee80211_i.h ++++ b/net/mac80211/ieee80211_i.h +@@ -1709,6 +1709,14 @@ struct ieee802_11_elems { + + /* whether a parse error occurred while retrieving these elements */ + bool parse_error; ++ ++ /* ++ * scratch buffer that can be used for various element parsing related ++ * tasks, e.g., element de-fragmentation etc. ++ */ ++ size_t scratch_len; ++ u8 *scratch_pos; ++ u8 scratch[]; + }; + + static inline struct ieee80211_local *hw_to_local( +diff --git a/net/mac80211/util.c b/net/mac80211/util.c +index f61289c5fed24..99e903299143e 100644 +--- a/net/mac80211/util.c ++++ b/net/mac80211/util.c +@@ -1506,24 +1506,26 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) + const struct element *non_inherit = NULL; + u8 *nontransmitted_profile; + int nontransmitted_profile_len = 0; ++ size_t scratch_len = params->len; + +- elems = kzalloc(sizeof(*elems), GFP_ATOMIC); ++ elems = kzalloc(sizeof(*elems) + scratch_len, GFP_ATOMIC); + if (!elems) + return NULL; + elems->ie_start = params->start; + elems->total_len = params->len; +- +- nontransmitted_profile = kmalloc(params->len, GFP_ATOMIC); +- if (nontransmitted_profile) { +- nontransmitted_profile_len = +- ieee802_11_find_bssid_profile(params->start, params->len, +- elems, params->bss, +- nontransmitted_profile); +- non_inherit = +- cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, +- nontransmitted_profile, +- nontransmitted_profile_len); +- } ++ elems->scratch_len = scratch_len; ++ elems->scratch_pos = elems->scratch; ++ ++ nontransmitted_profile = elems->scratch_pos; ++ nontransmitted_profile_len = ++ ieee802_11_find_bssid_profile(params->start, params->len, ++ elems, params->bss, ++ nontransmitted_profile); ++ elems->scratch_pos += nontransmitted_profile_len; ++ elems->scratch_len -= nontransmitted_profile_len; ++ non_inherit = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE, ++ nontransmitted_profile, ++ nontransmitted_profile_len); + + elems->crc = _ieee802_11_parse_elems_full(params, elems, non_inherit); + +@@ -1557,8 +1559,6 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params) + offsetofend(struct ieee80211_bssid_index, dtim_count)) + elems->dtim_count = elems->bssid_index->dtim_count; + +- kfree(nontransmitted_profile); +- + return elems; + } + +-- +cgit diff --git a/tests/cases/CVE-2022-42720.patch b/tests/cases/CVE-2022-42720.patch new file mode 100644 index 0000000..6043899 --- /dev/null +++ b/tests/cases/CVE-2022-42720.patch @@ -0,0 +1,92 @@ +From 0b7808818cb9df6680f98996b8e9a439fa7bcc2f Mon Sep 17 00:00:00 2001 +From: Johannes Berg +Date: Fri, 30 Sep 2022 23:44:23 +0200 +Subject: wifi: cfg80211: fix BSS refcounting bugs +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There are multiple refcounting bugs related to multi-BSSID: + - In bss_ref_get(), if the BSS has a hidden_beacon_bss, then + the bss pointer is overwritten before checking for the + transmitted BSS, which is clearly wrong. Fix this by using + the bss_from_pub() macro. + + - In cfg80211_bss_update() we copy the transmitted_bss pointer + from tmp into new, but then if we release new, we'll unref + it erroneously. We already set the pointer and ref it, but + need to NULL it since it was copied from the tmp data. + + - In cfg80211_inform_single_bss_data(), if adding to the non- + transmitted list fails, we unlink the BSS and yet still we + return it, but this results in returning an entry without + a reference. We shouldn't return it anyway if it was broken + enough to not get added there. + +This fixes CVE-2022-42720. + +Reported-by: Sönke Huster +Tested-by: Sönke Huster +Fixes: a3584f56de1c ("cfg80211: Properly track transmitting and non-transmitting BSS") +Signed-off-by: Johannes Berg +--- + net/wireless/scan.c | 27 ++++++++++++++------------- + 1 file changed, 14 insertions(+), 13 deletions(-) + +diff --git a/net/wireless/scan.c b/net/wireless/scan.c +index a183f2b758742..249107212c099 100644 +--- a/net/wireless/scan.c ++++ b/net/wireless/scan.c +@@ -143,18 +143,12 @@ static inline void bss_ref_get(struct cfg80211_registered_device *rdev, + lockdep_assert_held(&rdev->bss_lock); + + bss->refcount++; +- if (bss->pub.hidden_beacon_bss) { +- bss = container_of(bss->pub.hidden_beacon_bss, +- struct cfg80211_internal_bss, +- pub); +- bss->refcount++; +- } +- if (bss->pub.transmitted_bss) { +- bss = container_of(bss->pub.transmitted_bss, +- struct cfg80211_internal_bss, +- pub); +- bss->refcount++; +- } ++ ++ if (bss->pub.hidden_beacon_bss) ++ bss_from_pub(bss->pub.hidden_beacon_bss)->refcount++; ++ ++ if (bss->pub.transmitted_bss) ++ bss_from_pub(bss->pub.transmitted_bss)->refcount++; + } + + static inline void bss_ref_put(struct cfg80211_registered_device *rdev, +@@ -1741,6 +1735,8 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev, + new->refcount = 1; + INIT_LIST_HEAD(&new->hidden_list); + INIT_LIST_HEAD(&new->pub.nontrans_list); ++ /* we'll set this later if it was non-NULL */ ++ new->pub.transmitted_bss = NULL; + + if (rcu_access_pointer(tmp->pub.proberesp_ies)) { + hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_ZLEN); +@@ -2023,10 +2019,15 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy, + spin_lock_bh(&rdev->bss_lock); + if (cfg80211_add_nontrans_list(non_tx_data->tx_bss, + &res->pub)) { +- if (__cfg80211_unlink_bss(rdev, res)) ++ if (__cfg80211_unlink_bss(rdev, res)) { + rdev->bss_generation++; ++ res = NULL; ++ } + } + spin_unlock_bh(&rdev->bss_lock); ++ ++ if (!res) ++ return NULL; + } + + trace_cfg80211_return_bss(&res->pub); +-- +cgit diff --git a/tests/cases/CVE-2023-0045.patch b/tests/cases/CVE-2023-0045.patch new file mode 100644 index 0000000..bced66e --- /dev/null +++ b/tests/cases/CVE-2023-0045.patch @@ -0,0 +1,30 @@ +From a664ec9158eeddd75121d39c9a0758016097fa96 Mon Sep 17 00:00:00 2001 +From: Rodrigo Branco +Date: Tue, 3 Jan 2023 14:17:51 -0600 +Subject: x86/bugs: Flush IBP in ib_prctl_set() + +We missed the window between the TIF flag update and the next reschedule. + +Signed-off-by: Rodrigo Branco +Reviewed-by: Borislav Petkov (AMD) +Signed-off-by: Ingo Molnar +Cc: +--- + arch/x86/kernel/cpu/bugs.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c +index d970ddb0cc65b..bca0bd8f48464 100644 +--- a/arch/x86/kernel/cpu/bugs.c ++++ b/arch/x86/kernel/cpu/bugs.c +@@ -1981,6 +1981,8 @@ static int ib_prctl_set(struct task_struct *task, unsigned long ctrl) + if (ctrl == PR_SPEC_FORCE_DISABLE) + task_set_spec_ib_force_disable(task); + task_update_spec_tif(task); ++ if (task == current) ++ indirect_branch_prediction_barrier(); + break; + default: + return -ERANGE; +-- +cgit diff --git a/tests/cases/CVE-2023-0122.patch b/tests/cases/CVE-2023-0122.patch new file mode 100644 index 0000000..cf58029 --- /dev/null +++ b/tests/cases/CVE-2023-0122.patch @@ -0,0 +1,31 @@ +From da0342a3aa0357795224e6283df86444e1117168 Mon Sep 17 00:00:00 2001 +From: Hannes Reinecke +Date: Wed, 24 Aug 2022 09:23:16 +0200 +Subject: nvmet-auth: add missing goto in nvmet_setup_auth() + +There's a goto missing in nvmet_setup_auth(), causing a kernel oops +when nvme_auth_extract_key() fails. + +Reported-by: Tal Lossos +Signed-off-by: Hannes Reinecke +Reviewed-by: Sagi Grimberg +Reviewed-by: Chaitanya Kulkarni +Signed-off-by: Christoph Hellwig +--- + drivers/nvme/target/auth.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c +index cf690df347758..c4113b43dbfee 100644 +--- a/drivers/nvme/target/auth.c ++++ b/drivers/nvme/target/auth.c +@@ -196,6 +196,7 @@ int nvmet_setup_auth(struct nvmet_ctrl *ctrl) + if (IS_ERR(ctrl->ctrl_key)) { + ret = PTR_ERR(ctrl->ctrl_key); + ctrl->ctrl_key = NULL; ++ goto out_free_hash; + } + pr_debug("%s: using ctrl hash %s key %*ph\n", __func__, + ctrl->ctrl_key->hash > 0 ? +-- +cgit diff --git a/tests/cases/CVE-2023-0179.patch b/tests/cases/CVE-2023-0179.patch new file mode 100644 index 0000000..e7474bc --- /dev/null +++ b/tests/cases/CVE-2023-0179.patch @@ -0,0 +1,37 @@ +From 696e1a48b1a1b01edad542a1ef293665864a4dd0 Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Wed, 11 Jan 2023 17:07:33 +0100 +Subject: netfilter: nft_payload: incorrect arithmetics when fetching VLAN + header bits + +If the offset + length goes over the ethernet + vlan header, then the +length is adjusted to copy the bytes that are within the boundaries of +the vlan_ethhdr scratchpad area. The remaining bytes beyond ethernet + +vlan header are copied directly from the skbuff data area. + +Fix incorrect arithmetic operator: subtract, not add, the size of the +vlan header in case of double-tagged packets to adjust the length +accordingly to address CVE-2023-0179. + +Reported-by: Davide Ornaghi +Fixes: f6ae9f120dad ("netfilter: nft_payload: add C-VLAN support") +Signed-off-by: Pablo Neira Ayuso +--- + net/netfilter/nft_payload.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c +index 17b418a5a593a..3a3c7746e88fe 100644 +--- a/net/netfilter/nft_payload.c ++++ b/net/netfilter/nft_payload.c +@@ -63,7 +63,7 @@ nft_payload_copy_vlan(u32 *d, const struct sk_buff *skb, u8 offset, u8 len) + return false; + + if (offset + len > VLAN_ETH_HLEN + vlan_hlen) +- ethlen -= offset + len - VLAN_ETH_HLEN + vlan_hlen; ++ ethlen -= offset + len - VLAN_ETH_HLEN - vlan_hlen; + + memcpy(dst_u8, vlanh + offset - vlan_hlen, ethlen); + +-- +cgit diff --git a/tests/cases/CVE-2023-0210.patch b/tests/cases/CVE-2023-0210.patch new file mode 100644 index 0000000..9f8d0e5 --- /dev/null +++ b/tests/cases/CVE-2023-0210.patch @@ -0,0 +1,41 @@ +From 797805d81baa814f76cf7bdab35f86408a79d707 Mon Sep 17 00:00:00 2001 +From: William Liu +Date: Fri, 30 Dec 2022 13:03:15 +0900 +Subject: ksmbd: check nt_len to be at least CIFS_ENCPWD_SIZE in + ksmbd_decode_ntlmssp_auth_blob +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +"nt_len - CIFS_ENCPWD_SIZE" is passed directly from +ksmbd_decode_ntlmssp_auth_blob to ksmbd_auth_ntlmv2. Malicious requests +can set nt_len to less than CIFS_ENCPWD_SIZE, which results in a negative +number (or large unsigned value) used for a subsequent memcpy in +ksmbd_auth_ntlvm2 and can cause a panic. + +Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") +Cc: stable@vger.kernel.org +Signed-off-by: William Liu +Signed-off-by: Hrvoje Mišetić +Acked-by: Namjae Jeon +Signed-off-by: Steve French +--- + fs/ksmbd/auth.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/fs/ksmbd/auth.c b/fs/ksmbd/auth.c +index 2a39ffb8423b7..6e61b5bc7d86e 100644 +--- a/fs/ksmbd/auth.c ++++ b/fs/ksmbd/auth.c +@@ -322,7 +322,8 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, + dn_off = le32_to_cpu(authblob->DomainName.BufferOffset); + dn_len = le16_to_cpu(authblob->DomainName.Length); + +- if (blob_len < (u64)dn_off + dn_len || blob_len < (u64)nt_off + nt_len) ++ if (blob_len < (u64)dn_off + dn_len || blob_len < (u64)nt_off + nt_len || ++ nt_len < CIFS_ENCPWD_SIZE) + return -EINVAL; + + /* TODO : use domain name that imported from configuration file */ +-- +cgit diff --git a/tests/cases/CVE-2023-0386.patch b/tests/cases/CVE-2023-0386.patch new file mode 100644 index 0000000..6b5fcf6 --- /dev/null +++ b/tests/cases/CVE-2023-0386.patch @@ -0,0 +1,47 @@ +From 4f11ada10d0ad3fd53e2bd67806351de63a4f9c3 Mon Sep 17 00:00:00 2001 +From: Miklos Szeredi +Date: Tue, 24 Jan 2023 16:41:18 +0100 +Subject: ovl: fail on invalid uid/gid mapping at copy up + +If st_uid/st_gid doesn't have a mapping in the mounter's user_ns, then +copy-up should fail, just like it would fail if the mounter task was doing +the copy using "cp -a". + +There's a corner case where the "cp -a" would succeed but copy up fail: if +there's a mapping of the invalid uid/gid (65534 by default) in the user +namespace. This is because stat(2) will return this value if the mapping +doesn't exist in the current user_ns and "cp -a" will in turn be able to +create a file with this uid/gid. + +This behavior would be inconsistent with POSIX ACL's, which return -1 for +invalid uid/gid which result in a failed copy. + +For consistency and simplicity fail the copy of the st_uid/st_gid are +invalid. + +Fixes: 459c7c565ac3 ("ovl: unprivieged mounts") +Cc: # v5.11 +Signed-off-by: Miklos Szeredi +Reviewed-by: Christian Brauner +Reviewed-by: Seth Forshee +--- + fs/overlayfs/copy_up.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c +index 140f2742074d4..c14e90764e356 100644 +--- a/fs/overlayfs/copy_up.c ++++ b/fs/overlayfs/copy_up.c +@@ -1011,6 +1011,10 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, + if (err) + return err; + ++ if (!kuid_has_mapping(current_user_ns(), ctx.stat.uid) || ++ !kgid_has_mapping(current_user_ns(), ctx.stat.gid)) ++ return -EOVERFLOW; ++ + ctx.metacopy = ovl_need_meta_copy_up(dentry, ctx.stat.mode, flags); + + if (parent) { +-- +cgit diff --git a/tests/cases/CVE-2023-0461.patch b/tests/cases/CVE-2023-0461.patch new file mode 100644 index 0000000..ba6098a --- /dev/null +++ b/tests/cases/CVE-2023-0461.patch @@ -0,0 +1,78 @@ +From 2c02d41d71f90a5168391b6a5f2954112ba2307c Mon Sep 17 00:00:00 2001 +From: Paolo Abeni +Date: Tue, 3 Jan 2023 12:19:17 +0100 +Subject: net/ulp: prevent ULP without clone op from entering the LISTEN status + +When an ULP-enabled socket enters the LISTEN status, the listener ULP data +pointer is copied inside the child/accepted sockets by sk_clone_lock(). + +The relevant ULP can take care of de-duplicating the context pointer via +the clone() operation, but only MPTCP and SMC implement such op. + +Other ULPs may end-up with a double-free at socket disposal time. + +We can't simply clear the ULP data at clone time, as TLS replaces the +socket ops with custom ones assuming a valid TLS ULP context is +available. + +Instead completely prevent clone-less ULP sockets from entering the +LISTEN status. + +Fixes: 734942cc4ea6 ("tcp: ULP infrastructure") +Reported-by: slipper +Signed-off-by: Paolo Abeni +Link: https://lore.kernel.org/r/4b80c3d1dbe3d0ab072f80450c202d9bc88b4b03.1672740602.git.pabeni@redhat.com +Signed-off-by: Jakub Kicinski +--- + net/ipv4/inet_connection_sock.c | 14 ++++++++++++++ + net/ipv4/tcp_ulp.c | 4 ++++ + 2 files changed, 18 insertions(+) + +diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c +index 848ffc3e0239c3..d1f83757939830 100644 +--- a/net/ipv4/inet_connection_sock.c ++++ b/net/ipv4/inet_connection_sock.c +@@ -1200,12 +1200,26 @@ void inet_csk_prepare_forced_close(struct sock *sk) + } + EXPORT_SYMBOL(inet_csk_prepare_forced_close); + ++static int inet_ulp_can_listen(const struct sock *sk) ++{ ++ const struct inet_connection_sock *icsk = inet_csk(sk); ++ ++ if (icsk->icsk_ulp_ops && !icsk->icsk_ulp_ops->clone) ++ return -EINVAL; ++ ++ return 0; ++} ++ + int inet_csk_listen_start(struct sock *sk) + { + struct inet_connection_sock *icsk = inet_csk(sk); + struct inet_sock *inet = inet_sk(sk); + int err; + ++ err = inet_ulp_can_listen(sk); ++ if (unlikely(err)) ++ return err; ++ + reqsk_queue_alloc(&icsk->icsk_accept_queue); + + sk->sk_ack_backlog = 0; +diff --git a/net/ipv4/tcp_ulp.c b/net/ipv4/tcp_ulp.c +index 9ae50b1bd84441..05b6077b9f2c35 100644 +--- a/net/ipv4/tcp_ulp.c ++++ b/net/ipv4/tcp_ulp.c +@@ -139,6 +139,10 @@ static int __tcp_set_ulp(struct sock *sk, const struct tcp_ulp_ops *ulp_ops) + if (sk->sk_socket) + clear_bit(SOCK_SUPPORT_ZC, &sk->sk_socket->flags); + ++ err = -EINVAL; ++ if (!ulp_ops->clone && sk->sk_state == TCP_LISTEN) ++ goto out_err; ++ + err = ulp_ops->init(sk); + if (err) + goto out_err; +-- +cgit 1.2.3-korg diff --git a/tests/cases/CVE-2023-1829.patch b/tests/cases/CVE-2023-1829.patch new file mode 100644 index 0000000..3c1d439 --- /dev/null +++ b/tests/cases/CVE-2023-1829.patch @@ -0,0 +1,1036 @@ +From 8c710f75256bb3cf05ac7b1672c82b92c43f3d28 Mon Sep 17 00:00:00 2001 +From: Jamal Hadi Salim +Date: Tue, 14 Feb 2023 08:49:14 -0500 +Subject: net/sched: Retire tcindex classifier + +The tcindex classifier has served us well for about a quarter of a century +but has not been getting much TLC due to lack of known users. Most recently +it has become easy prey to syzkaller. For this reason, we are retiring it. + +Signed-off-by: Jamal Hadi Salim +Acked-by: Jiri Pirko +Signed-off-by: Paolo Abeni +--- + include/net/tc_wrapper.h | 5 - + net/sched/Kconfig | 11 - + net/sched/Makefile | 1 - + net/sched/cls_tcindex.c | 716 --------------------- + .../tc-testing/tc-tests/filters/tcindex.json | 227 ------- + 5 files changed, 960 deletions(-) + delete mode 100644 net/sched/cls_tcindex.c + delete mode 100644 tools/testing/selftests/tc-testing/tc-tests/filters/tcindex.json + +diff --git a/include/net/tc_wrapper.h b/include/net/tc_wrapper.h +index d323fffb839aa..8ba241760d0af 100644 +--- a/include/net/tc_wrapper.h ++++ b/include/net/tc_wrapper.h +@@ -154,7 +154,6 @@ TC_INDIRECT_FILTER_DECLARE(mall_classify); + TC_INDIRECT_FILTER_DECLARE(route4_classify); + TC_INDIRECT_FILTER_DECLARE(rsvp_classify); + TC_INDIRECT_FILTER_DECLARE(rsvp6_classify); +-TC_INDIRECT_FILTER_DECLARE(tcindex_classify); + TC_INDIRECT_FILTER_DECLARE(u32_classify); + + static inline int tc_classify(struct sk_buff *skb, const struct tcf_proto *tp, +@@ -207,10 +206,6 @@ static inline int tc_classify(struct sk_buff *skb, const struct tcf_proto *tp, + if (tp->classify == rsvp6_classify) + return rsvp6_classify(skb, tp, res); + #endif +-#if IS_BUILTIN(CONFIG_NET_CLS_TCINDEX) +- if (tp->classify == tcindex_classify) +- return tcindex_classify(skb, tp, res); +-#endif + + skip: + return tp->classify(skb, tp, res); +diff --git a/net/sched/Kconfig b/net/sched/Kconfig +index d909f289a67fd..111883853f08c 100644 +--- a/net/sched/Kconfig ++++ b/net/sched/Kconfig +@@ -468,17 +468,6 @@ config NET_CLS_BASIC + To compile this code as a module, choose M here: the + module will be called cls_basic. + +-config NET_CLS_TCINDEX +- tristate "Traffic-Control Index (TCINDEX)" +- select NET_CLS +- help +- Say Y here if you want to be able to classify packets based on +- traffic control indices. You will want this feature if you want +- to implement Differentiated Services together with DSMARK. +- +- To compile this code as a module, choose M here: the +- module will be called cls_tcindex. +- + config NET_CLS_ROUTE4 + tristate "Routing decision (ROUTE)" + depends on INET +diff --git a/net/sched/Makefile b/net/sched/Makefile +index 0852e989af96b..ea236d258c165 100644 +--- a/net/sched/Makefile ++++ b/net/sched/Makefile +@@ -68,7 +68,6 @@ obj-$(CONFIG_NET_CLS_U32) += cls_u32.o + obj-$(CONFIG_NET_CLS_ROUTE4) += cls_route.o + obj-$(CONFIG_NET_CLS_FW) += cls_fw.o + obj-$(CONFIG_NET_CLS_RSVP) += cls_rsvp.o +-obj-$(CONFIG_NET_CLS_TCINDEX) += cls_tcindex.o + obj-$(CONFIG_NET_CLS_RSVP6) += cls_rsvp6.o + obj-$(CONFIG_NET_CLS_BASIC) += cls_basic.o + obj-$(CONFIG_NET_CLS_FLOW) += cls_flow.o +diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c +deleted file mode 100644 +index ee2a050c887bf..0000000000000 +--- a/net/sched/cls_tcindex.c ++++ /dev/null +@@ -1,716 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0-only +-/* +- * net/sched/cls_tcindex.c Packet classifier for skb->tc_index +- * +- * Written 1998,1999 by Werner Almesberger, EPFL ICA +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-/* +- * Passing parameters to the root seems to be done more awkwardly than really +- * necessary. At least, u32 doesn't seem to use such dirty hacks. To be +- * verified. FIXME. +- */ +- +-#define PERFECT_HASH_THRESHOLD 64 /* use perfect hash if not bigger */ +-#define DEFAULT_HASH_SIZE 64 /* optimized for diffserv */ +- +- +-struct tcindex_data; +- +-struct tcindex_filter_result { +- struct tcf_exts exts; +- struct tcf_result res; +- struct tcindex_data *p; +- struct rcu_work rwork; +-}; +- +-struct tcindex_filter { +- u16 key; +- struct tcindex_filter_result result; +- struct tcindex_filter __rcu *next; +- struct rcu_work rwork; +-}; +- +- +-struct tcindex_data { +- struct tcindex_filter_result *perfect; /* perfect hash; NULL if none */ +- struct tcindex_filter __rcu **h; /* imperfect hash; */ +- struct tcf_proto *tp; +- u16 mask; /* AND key with mask */ +- u32 shift; /* shift ANDed key to the right */ +- u32 hash; /* hash table size; 0 if undefined */ +- u32 alloc_hash; /* allocated size */ +- u32 fall_through; /* 0: only classify if explicit match */ +- refcount_t refcnt; /* a temporary refcnt for perfect hash */ +- struct rcu_work rwork; +-}; +- +-static inline int tcindex_filter_is_set(struct tcindex_filter_result *r) +-{ +- return tcf_exts_has_actions(&r->exts) || r->res.classid; +-} +- +-static void tcindex_data_get(struct tcindex_data *p) +-{ +- refcount_inc(&p->refcnt); +-} +- +-static void tcindex_data_put(struct tcindex_data *p) +-{ +- if (refcount_dec_and_test(&p->refcnt)) { +- kfree(p->perfect); +- kfree(p->h); +- kfree(p); +- } +-} +- +-static struct tcindex_filter_result *tcindex_lookup(struct tcindex_data *p, +- u16 key) +-{ +- if (p->perfect) { +- struct tcindex_filter_result *f = p->perfect + key; +- +- return tcindex_filter_is_set(f) ? f : NULL; +- } else if (p->h) { +- struct tcindex_filter __rcu **fp; +- struct tcindex_filter *f; +- +- fp = &p->h[key % p->hash]; +- for (f = rcu_dereference_bh_rtnl(*fp); +- f; +- fp = &f->next, f = rcu_dereference_bh_rtnl(*fp)) +- if (f->key == key) +- return &f->result; +- } +- +- return NULL; +-} +- +-TC_INDIRECT_SCOPE int tcindex_classify(struct sk_buff *skb, +- const struct tcf_proto *tp, +- struct tcf_result *res) +-{ +- struct tcindex_data *p = rcu_dereference_bh(tp->root); +- struct tcindex_filter_result *f; +- int key = (skb->tc_index & p->mask) >> p->shift; +- +- pr_debug("tcindex_classify(skb %p,tp %p,res %p),p %p\n", +- skb, tp, res, p); +- +- f = tcindex_lookup(p, key); +- if (!f) { +- struct Qdisc *q = tcf_block_q(tp->chain->block); +- +- if (!p->fall_through) +- return -1; +- res->classid = TC_H_MAKE(TC_H_MAJ(q->handle), key); +- res->class = 0; +- pr_debug("alg 0x%x\n", res->classid); +- return 0; +- } +- *res = f->res; +- pr_debug("map 0x%x\n", res->classid); +- +- return tcf_exts_exec(skb, &f->exts, res); +-} +- +- +-static void *tcindex_get(struct tcf_proto *tp, u32 handle) +-{ +- struct tcindex_data *p = rtnl_dereference(tp->root); +- struct tcindex_filter_result *r; +- +- pr_debug("tcindex_get(tp %p,handle 0x%08x)\n", tp, handle); +- if (p->perfect && handle >= p->alloc_hash) +- return NULL; +- r = tcindex_lookup(p, handle); +- return r && tcindex_filter_is_set(r) ? r : NULL; +-} +- +-static int tcindex_init(struct tcf_proto *tp) +-{ +- struct tcindex_data *p; +- +- pr_debug("tcindex_init(tp %p)\n", tp); +- p = kzalloc(sizeof(struct tcindex_data), GFP_KERNEL); +- if (!p) +- return -ENOMEM; +- +- p->mask = 0xffff; +- p->hash = DEFAULT_HASH_SIZE; +- p->fall_through = 1; +- refcount_set(&p->refcnt, 1); /* Paired with tcindex_destroy_work() */ +- +- rcu_assign_pointer(tp->root, p); +- return 0; +-} +- +-static void __tcindex_destroy_rexts(struct tcindex_filter_result *r) +-{ +- tcf_exts_destroy(&r->exts); +- tcf_exts_put_net(&r->exts); +- tcindex_data_put(r->p); +-} +- +-static void tcindex_destroy_rexts_work(struct work_struct *work) +-{ +- struct tcindex_filter_result *r; +- +- r = container_of(to_rcu_work(work), +- struct tcindex_filter_result, +- rwork); +- rtnl_lock(); +- __tcindex_destroy_rexts(r); +- rtnl_unlock(); +-} +- +-static void __tcindex_destroy_fexts(struct tcindex_filter *f) +-{ +- tcf_exts_destroy(&f->result.exts); +- tcf_exts_put_net(&f->result.exts); +- kfree(f); +-} +- +-static void tcindex_destroy_fexts_work(struct work_struct *work) +-{ +- struct tcindex_filter *f = container_of(to_rcu_work(work), +- struct tcindex_filter, +- rwork); +- +- rtnl_lock(); +- __tcindex_destroy_fexts(f); +- rtnl_unlock(); +-} +- +-static int tcindex_delete(struct tcf_proto *tp, void *arg, bool *last, +- bool rtnl_held, struct netlink_ext_ack *extack) +-{ +- struct tcindex_data *p = rtnl_dereference(tp->root); +- struct tcindex_filter_result *r = arg; +- struct tcindex_filter __rcu **walk; +- struct tcindex_filter *f = NULL; +- +- pr_debug("tcindex_delete(tp %p,arg %p),p %p\n", tp, arg, p); +- if (p->perfect) { +- if (!r->res.class) +- return -ENOENT; +- } else { +- int i; +- +- for (i = 0; i < p->hash; i++) { +- walk = p->h + i; +- for (f = rtnl_dereference(*walk); f; +- walk = &f->next, f = rtnl_dereference(*walk)) { +- if (&f->result == r) +- goto found; +- } +- } +- return -ENOENT; +- +-found: +- rcu_assign_pointer(*walk, rtnl_dereference(f->next)); +- } +- tcf_unbind_filter(tp, &r->res); +- /* all classifiers are required to call tcf_exts_destroy() after rcu +- * grace period, since converted-to-rcu actions are relying on that +- * in cleanup() callback +- */ +- if (f) { +- if (tcf_exts_get_net(&f->result.exts)) +- tcf_queue_work(&f->rwork, tcindex_destroy_fexts_work); +- else +- __tcindex_destroy_fexts(f); +- } else { +- tcindex_data_get(p); +- +- if (tcf_exts_get_net(&r->exts)) +- tcf_queue_work(&r->rwork, tcindex_destroy_rexts_work); +- else +- __tcindex_destroy_rexts(r); +- } +- +- *last = false; +- return 0; +-} +- +-static void tcindex_destroy_work(struct work_struct *work) +-{ +- struct tcindex_data *p = container_of(to_rcu_work(work), +- struct tcindex_data, +- rwork); +- +- tcindex_data_put(p); +-} +- +-static inline int +-valid_perfect_hash(struct tcindex_data *p) +-{ +- return p->hash > (p->mask >> p->shift); +-} +- +-static const struct nla_policy tcindex_policy[TCA_TCINDEX_MAX + 1] = { +- [TCA_TCINDEX_HASH] = { .type = NLA_U32 }, +- [TCA_TCINDEX_MASK] = { .type = NLA_U16 }, +- [TCA_TCINDEX_SHIFT] = { .type = NLA_U32 }, +- [TCA_TCINDEX_FALL_THROUGH] = { .type = NLA_U32 }, +- [TCA_TCINDEX_CLASSID] = { .type = NLA_U32 }, +-}; +- +-static int tcindex_filter_result_init(struct tcindex_filter_result *r, +- struct tcindex_data *p, +- struct net *net) +-{ +- memset(r, 0, sizeof(*r)); +- r->p = p; +- return tcf_exts_init(&r->exts, net, TCA_TCINDEX_ACT, +- TCA_TCINDEX_POLICE); +-} +- +-static void tcindex_free_perfect_hash(struct tcindex_data *cp); +- +-static void tcindex_partial_destroy_work(struct work_struct *work) +-{ +- struct tcindex_data *p = container_of(to_rcu_work(work), +- struct tcindex_data, +- rwork); +- +- rtnl_lock(); +- if (p->perfect) +- tcindex_free_perfect_hash(p); +- kfree(p); +- rtnl_unlock(); +-} +- +-static void tcindex_free_perfect_hash(struct tcindex_data *cp) +-{ +- int i; +- +- for (i = 0; i < cp->hash; i++) +- tcf_exts_destroy(&cp->perfect[i].exts); +- kfree(cp->perfect); +-} +- +-static int tcindex_alloc_perfect_hash(struct net *net, struct tcindex_data *cp) +-{ +- int i, err = 0; +- +- cp->perfect = kcalloc(cp->hash, sizeof(struct tcindex_filter_result), +- GFP_KERNEL | __GFP_NOWARN); +- if (!cp->perfect) +- return -ENOMEM; +- +- for (i = 0; i < cp->hash; i++) { +- err = tcf_exts_init(&cp->perfect[i].exts, net, +- TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE); +- if (err < 0) +- goto errout; +- cp->perfect[i].p = cp; +- } +- +- return 0; +- +-errout: +- tcindex_free_perfect_hash(cp); +- return err; +-} +- +-static int +-tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, +- u32 handle, struct tcindex_data *p, +- struct tcindex_filter_result *r, struct nlattr **tb, +- struct nlattr *est, u32 flags, struct netlink_ext_ack *extack) +-{ +- struct tcindex_filter_result new_filter_result; +- struct tcindex_data *cp = NULL, *oldp; +- struct tcindex_filter *f = NULL; /* make gcc behave */ +- struct tcf_result cr = {}; +- int err, balloc = 0; +- struct tcf_exts e; +- +- err = tcf_exts_init(&e, net, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE); +- if (err < 0) +- return err; +- err = tcf_exts_validate(net, tp, tb, est, &e, flags, extack); +- if (err < 0) +- goto errout; +- +- err = -ENOMEM; +- /* tcindex_data attributes must look atomic to classifier/lookup so +- * allocate new tcindex data and RCU assign it onto root. Keeping +- * perfect hash and hash pointers from old data. +- */ +- cp = kzalloc(sizeof(*cp), GFP_KERNEL); +- if (!cp) +- goto errout; +- +- cp->mask = p->mask; +- cp->shift = p->shift; +- cp->hash = p->hash; +- cp->alloc_hash = p->alloc_hash; +- cp->fall_through = p->fall_through; +- cp->tp = tp; +- refcount_set(&cp->refcnt, 1); /* Paired with tcindex_destroy_work() */ +- +- if (tb[TCA_TCINDEX_HASH]) +- cp->hash = nla_get_u32(tb[TCA_TCINDEX_HASH]); +- +- if (tb[TCA_TCINDEX_MASK]) +- cp->mask = nla_get_u16(tb[TCA_TCINDEX_MASK]); +- +- if (tb[TCA_TCINDEX_SHIFT]) { +- cp->shift = nla_get_u32(tb[TCA_TCINDEX_SHIFT]); +- if (cp->shift > 16) { +- err = -EINVAL; +- goto errout; +- } +- } +- if (!cp->hash) { +- /* Hash not specified, use perfect hash if the upper limit +- * of the hashing index is below the threshold. +- */ +- if ((cp->mask >> cp->shift) < PERFECT_HASH_THRESHOLD) +- cp->hash = (cp->mask >> cp->shift) + 1; +- else +- cp->hash = DEFAULT_HASH_SIZE; +- } +- +- if (p->perfect) { +- int i; +- +- if (tcindex_alloc_perfect_hash(net, cp) < 0) +- goto errout; +- cp->alloc_hash = cp->hash; +- for (i = 0; i < min(cp->hash, p->hash); i++) +- cp->perfect[i].res = p->perfect[i].res; +- balloc = 1; +- } +- cp->h = p->h; +- +- err = tcindex_filter_result_init(&new_filter_result, cp, net); +- if (err < 0) +- goto errout_alloc; +- if (r) +- cr = r->res; +- +- err = -EBUSY; +- +- /* Hash already allocated, make sure that we still meet the +- * requirements for the allocated hash. +- */ +- if (cp->perfect) { +- if (!valid_perfect_hash(cp) || +- cp->hash > cp->alloc_hash) +- goto errout_alloc; +- } else if (cp->h && cp->hash != cp->alloc_hash) { +- goto errout_alloc; +- } +- +- err = -EINVAL; +- if (tb[TCA_TCINDEX_FALL_THROUGH]) +- cp->fall_through = nla_get_u32(tb[TCA_TCINDEX_FALL_THROUGH]); +- +- if (!cp->perfect && !cp->h) +- cp->alloc_hash = cp->hash; +- +- /* Note: this could be as restrictive as if (handle & ~(mask >> shift)) +- * but then, we'd fail handles that may become valid after some future +- * mask change. While this is extremely unlikely to ever matter, +- * the check below is safer (and also more backwards-compatible). +- */ +- if (cp->perfect || valid_perfect_hash(cp)) +- if (handle >= cp->alloc_hash) +- goto errout_alloc; +- +- +- err = -ENOMEM; +- if (!cp->perfect && !cp->h) { +- if (valid_perfect_hash(cp)) { +- if (tcindex_alloc_perfect_hash(net, cp) < 0) +- goto errout_alloc; +- balloc = 1; +- } else { +- struct tcindex_filter __rcu **hash; +- +- hash = kcalloc(cp->hash, +- sizeof(struct tcindex_filter *), +- GFP_KERNEL); +- +- if (!hash) +- goto errout_alloc; +- +- cp->h = hash; +- balloc = 2; +- } +- } +- +- if (cp->perfect) +- r = cp->perfect + handle; +- else +- r = tcindex_lookup(cp, handle) ? : &new_filter_result; +- +- if (r == &new_filter_result) { +- f = kzalloc(sizeof(*f), GFP_KERNEL); +- if (!f) +- goto errout_alloc; +- f->key = handle; +- f->next = NULL; +- err = tcindex_filter_result_init(&f->result, cp, net); +- if (err < 0) { +- kfree(f); +- goto errout_alloc; +- } +- } +- +- if (tb[TCA_TCINDEX_CLASSID]) { +- cr.classid = nla_get_u32(tb[TCA_TCINDEX_CLASSID]); +- tcf_bind_filter(tp, &cr, base); +- } +- +- oldp = p; +- r->res = cr; +- tcf_exts_change(&r->exts, &e); +- +- rcu_assign_pointer(tp->root, cp); +- +- if (r == &new_filter_result) { +- struct tcindex_filter *nfp; +- struct tcindex_filter __rcu **fp; +- +- f->result.res = r->res; +- tcf_exts_change(&f->result.exts, &r->exts); +- +- fp = cp->h + (handle % cp->hash); +- for (nfp = rtnl_dereference(*fp); +- nfp; +- fp = &nfp->next, nfp = rtnl_dereference(*fp)) +- ; /* nothing */ +- +- rcu_assign_pointer(*fp, f); +- } else { +- tcf_exts_destroy(&new_filter_result.exts); +- } +- +- if (oldp) +- tcf_queue_work(&oldp->rwork, tcindex_partial_destroy_work); +- return 0; +- +-errout_alloc: +- if (balloc == 1) +- tcindex_free_perfect_hash(cp); +- else if (balloc == 2) +- kfree(cp->h); +- tcf_exts_destroy(&new_filter_result.exts); +-errout: +- kfree(cp); +- tcf_exts_destroy(&e); +- return err; +-} +- +-static int +-tcindex_change(struct net *net, struct sk_buff *in_skb, +- struct tcf_proto *tp, unsigned long base, u32 handle, +- struct nlattr **tca, void **arg, u32 flags, +- struct netlink_ext_ack *extack) +-{ +- struct nlattr *opt = tca[TCA_OPTIONS]; +- struct nlattr *tb[TCA_TCINDEX_MAX + 1]; +- struct tcindex_data *p = rtnl_dereference(tp->root); +- struct tcindex_filter_result *r = *arg; +- int err; +- +- pr_debug("tcindex_change(tp %p,handle 0x%08x,tca %p,arg %p),opt %p," +- "p %p,r %p,*arg %p\n", +- tp, handle, tca, arg, opt, p, r, *arg); +- +- if (!opt) +- return 0; +- +- err = nla_parse_nested_deprecated(tb, TCA_TCINDEX_MAX, opt, +- tcindex_policy, NULL); +- if (err < 0) +- return err; +- +- return tcindex_set_parms(net, tp, base, handle, p, r, tb, +- tca[TCA_RATE], flags, extack); +-} +- +-static void tcindex_walk(struct tcf_proto *tp, struct tcf_walker *walker, +- bool rtnl_held) +-{ +- struct tcindex_data *p = rtnl_dereference(tp->root); +- struct tcindex_filter *f, *next; +- int i; +- +- pr_debug("tcindex_walk(tp %p,walker %p),p %p\n", tp, walker, p); +- if (p->perfect) { +- for (i = 0; i < p->hash; i++) { +- if (!p->perfect[i].res.class) +- continue; +- if (!tc_cls_stats_dump(tp, walker, p->perfect + i)) +- return; +- } +- } +- if (!p->h) +- return; +- for (i = 0; i < p->hash; i++) { +- for (f = rtnl_dereference(p->h[i]); f; f = next) { +- next = rtnl_dereference(f->next); +- if (!tc_cls_stats_dump(tp, walker, &f->result)) +- return; +- } +- } +-} +- +-static void tcindex_destroy(struct tcf_proto *tp, bool rtnl_held, +- struct netlink_ext_ack *extack) +-{ +- struct tcindex_data *p = rtnl_dereference(tp->root); +- int i; +- +- pr_debug("tcindex_destroy(tp %p),p %p\n", tp, p); +- +- if (p->perfect) { +- for (i = 0; i < p->hash; i++) { +- struct tcindex_filter_result *r = p->perfect + i; +- +- /* tcf_queue_work() does not guarantee the ordering we +- * want, so we have to take this refcnt temporarily to +- * ensure 'p' is freed after all tcindex_filter_result +- * here. Imperfect hash does not need this, because it +- * uses linked lists rather than an array. +- */ +- tcindex_data_get(p); +- +- tcf_unbind_filter(tp, &r->res); +- if (tcf_exts_get_net(&r->exts)) +- tcf_queue_work(&r->rwork, +- tcindex_destroy_rexts_work); +- else +- __tcindex_destroy_rexts(r); +- } +- } +- +- for (i = 0; p->h && i < p->hash; i++) { +- struct tcindex_filter *f, *next; +- bool last; +- +- for (f = rtnl_dereference(p->h[i]); f; f = next) { +- next = rtnl_dereference(f->next); +- tcindex_delete(tp, &f->result, &last, rtnl_held, NULL); +- } +- } +- +- tcf_queue_work(&p->rwork, tcindex_destroy_work); +-} +- +- +-static int tcindex_dump(struct net *net, struct tcf_proto *tp, void *fh, +- struct sk_buff *skb, struct tcmsg *t, bool rtnl_held) +-{ +- struct tcindex_data *p = rtnl_dereference(tp->root); +- struct tcindex_filter_result *r = fh; +- struct nlattr *nest; +- +- pr_debug("tcindex_dump(tp %p,fh %p,skb %p,t %p),p %p,r %p\n", +- tp, fh, skb, t, p, r); +- pr_debug("p->perfect %p p->h %p\n", p->perfect, p->h); +- +- nest = nla_nest_start_noflag(skb, TCA_OPTIONS); +- if (nest == NULL) +- goto nla_put_failure; +- +- if (!fh) { +- t->tcm_handle = ~0; /* whatever ... */ +- if (nla_put_u32(skb, TCA_TCINDEX_HASH, p->hash) || +- nla_put_u16(skb, TCA_TCINDEX_MASK, p->mask) || +- nla_put_u32(skb, TCA_TCINDEX_SHIFT, p->shift) || +- nla_put_u32(skb, TCA_TCINDEX_FALL_THROUGH, p->fall_through)) +- goto nla_put_failure; +- nla_nest_end(skb, nest); +- } else { +- if (p->perfect) { +- t->tcm_handle = r - p->perfect; +- } else { +- struct tcindex_filter *f; +- struct tcindex_filter __rcu **fp; +- int i; +- +- t->tcm_handle = 0; +- for (i = 0; !t->tcm_handle && i < p->hash; i++) { +- fp = &p->h[i]; +- for (f = rtnl_dereference(*fp); +- !t->tcm_handle && f; +- fp = &f->next, f = rtnl_dereference(*fp)) { +- if (&f->result == r) +- t->tcm_handle = f->key; +- } +- } +- } +- pr_debug("handle = %d\n", t->tcm_handle); +- if (r->res.class && +- nla_put_u32(skb, TCA_TCINDEX_CLASSID, r->res.classid)) +- goto nla_put_failure; +- +- if (tcf_exts_dump(skb, &r->exts) < 0) +- goto nla_put_failure; +- nla_nest_end(skb, nest); +- +- if (tcf_exts_dump_stats(skb, &r->exts) < 0) +- goto nla_put_failure; +- } +- +- return skb->len; +- +-nla_put_failure: +- nla_nest_cancel(skb, nest); +- return -1; +-} +- +-static void tcindex_bind_class(void *fh, u32 classid, unsigned long cl, +- void *q, unsigned long base) +-{ +- struct tcindex_filter_result *r = fh; +- +- tc_cls_bind_class(classid, cl, q, &r->res, base); +-} +- +-static struct tcf_proto_ops cls_tcindex_ops __read_mostly = { +- .kind = "tcindex", +- .classify = tcindex_classify, +- .init = tcindex_init, +- .destroy = tcindex_destroy, +- .get = tcindex_get, +- .change = tcindex_change, +- .delete = tcindex_delete, +- .walk = tcindex_walk, +- .dump = tcindex_dump, +- .bind_class = tcindex_bind_class, +- .owner = THIS_MODULE, +-}; +- +-static int __init init_tcindex(void) +-{ +- return register_tcf_proto_ops(&cls_tcindex_ops); +-} +- +-static void __exit exit_tcindex(void) +-{ +- unregister_tcf_proto_ops(&cls_tcindex_ops); +-} +- +-module_init(init_tcindex) +-module_exit(exit_tcindex) +-MODULE_LICENSE("GPL"); +diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/tcindex.json b/tools/testing/selftests/tc-testing/tc-tests/filters/tcindex.json +deleted file mode 100644 +index 44901db703764..0000000000000 +--- a/tools/testing/selftests/tc-testing/tc-tests/filters/tcindex.json ++++ /dev/null +@@ -1,227 +0,0 @@ +-[ +- { +- "id": "8293", +- "name": "Add tcindex filter with default action", +- "category": [ +- "filter", +- "tcindex" +- ], +- "plugins": { +- "requires": "nsPlugin" +- }, +- "setup": [ +- "$TC qdisc add dev $DEV1 ingress" +- ], +- "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 protocol ip prio 1 tcindex classid 1:1", +- "expExitCode": "0", +- "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol ip tcindex", +- "matchPattern": "^filter parent ffff: protocol ip pref 1 tcindex chain 0 handle 0x0001 classid 1:1", +- "matchCount": "1", +- "teardown": [ +- "$TC qdisc del dev $DEV1 ingress" +- ] +- }, +- { +- "id": "7281", +- "name": "Add tcindex filter with hash size and pass action", +- "category": [ +- "filter", +- "tcindex" +- ], +- "plugins": { +- "requires": "nsPlugin" +- }, +- "setup": [ +- "$TC qdisc add dev $DEV1 ingress" +- ], +- "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 protocol ip prio 1 tcindex hash 32 fall_through classid 1:1 action pass", +- "expExitCode": "0", +- "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol ip tcindex", +- "matchPattern": "^filter parent ffff: protocol ip pref.*tcindex chain [0-9]+ handle 0x0001 classid 1:1.*action order [0-9]+: gact action pass", +- "matchCount": "1", +- "teardown": [ +- "$TC qdisc del dev $DEV1 ingress" +- ] +- }, +- { +- "id": "b294", +- "name": "Add tcindex filter with mask shift and reclassify action", +- "category": [ +- "filter", +- "tcindex" +- ], +- "plugins": { +- "requires": "nsPlugin" +- }, +- "setup": [ +- "$TC qdisc add dev $DEV1 ingress" +- ], +- "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 protocol ip prio 1 tcindex hash 32 mask 1 shift 2 fall_through classid 1:1 action reclassify", +- "expExitCode": "0", +- "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol ip tcindex", +- "matchPattern": "^filter parent ffff: protocol ip pref.*tcindex chain [0-9]+ handle 0x0001 classid 1:1.*action order [0-9]+: gact action reclassify", +- "matchCount": "1", +- "teardown": [ +- "$TC qdisc del dev $DEV1 ingress" +- ] +- }, +- { +- "id": "0532", +- "name": "Add tcindex filter with pass_on and continue actions", +- "category": [ +- "filter", +- "tcindex" +- ], +- "plugins": { +- "requires": "nsPlugin" +- }, +- "setup": [ +- "$TC qdisc add dev $DEV1 ingress" +- ], +- "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 protocol ip prio 1 tcindex hash 32 mask 1 shift 2 pass_on classid 1:1 action continue", +- "expExitCode": "0", +- "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol ip tcindex", +- "matchPattern": "^filter parent ffff: protocol ip pref.*tcindex chain [0-9]+ handle 0x0001 classid 1:1.*action order [0-9]+: gact action continue", +- "matchCount": "1", +- "teardown": [ +- "$TC qdisc del dev $DEV1 ingress" +- ] +- }, +- { +- "id": "d473", +- "name": "Add tcindex filter with pipe action", +- "category": [ +- "filter", +- "tcindex" +- ], +- "plugins": { +- "requires": "nsPlugin" +- }, +- "setup": [ +- "$TC qdisc add dev $DEV1 ingress" +- ], +- "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 protocol ip prio 1 tcindex hash 32 mask 1 shift 2 fall_through classid 1:1 action pipe", +- "expExitCode": "0", +- "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol ip tcindex", +- "matchPattern": "^filter parent ffff: protocol ip pref.*tcindex chain [0-9]+ handle 0x0001 classid 1:1.*action order [0-9]+: gact action pipe", +- "matchCount": "1", +- "teardown": [ +- "$TC qdisc del dev $DEV1 ingress" +- ] +- }, +- { +- "id": "2940", +- "name": "Add tcindex filter with miltiple actions", +- "category": [ +- "filter", +- "tcindex" +- ], +- "plugins": { +- "requires": "nsPlugin" +- }, +- "setup": [ +- "$TC qdisc add dev $DEV1 ingress" +- ], +- "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: handle 1 protocol ip prio 7 tcindex hash 32 mask 1 shift 2 fall_through classid 1:1 action skbedit mark 7 pipe action gact drop", +- "expExitCode": "0", +- "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 7 protocol ip tcindex", +- "matchPattern": "^filter parent ffff: protocol ip pref 7 tcindex.*handle 0x0001.*action.*skbedit.*mark 7 pipe.*action.*gact action drop", +- "matchCount": "1", +- "teardown": [ +- "$TC qdisc del dev $DEV1 ingress" +- ] +- }, +- { +- "id": "1893", +- "name": "List tcindex filters", +- "category": [ +- "filter", +- "tcindex" +- ], +- "plugins": { +- "requires": "nsPlugin" +- }, +- "setup": [ +- "$TC qdisc add dev $DEV1 ingress", +- "$TC filter add dev $DEV1 parent ffff: handle 1 protocol ip prio 1 tcindex classid 1:1", +- "$TC filter add dev $DEV1 parent ffff: handle 2 protocol ip prio 1 tcindex classid 1:1" +- ], +- "cmdUnderTest": "$TC filter show dev $DEV1 parent ffff:", +- "expExitCode": "0", +- "verifyCmd": "$TC filter show dev $DEV1 parent ffff:", +- "matchPattern": "handle 0x000[0-9]+ classid 1:1", +- "matchCount": "2", +- "teardown": [ +- "$TC qdisc del dev $DEV1 ingress" +- ] +- }, +- { +- "id": "2041", +- "name": "Change tcindex filter with pass action", +- "category": [ +- "filter", +- "tcindex" +- ], +- "plugins": { +- "requires": "nsPlugin" +- }, +- "setup": [ +- "$TC qdisc add dev $DEV1 ingress", +- "$TC filter add dev $DEV1 parent ffff: handle 1 protocol ip prio 1 tcindex classid 1:1 action drop" +- ], +- "cmdUnderTest": "$TC filter change dev $DEV1 parent ffff: handle 1 protocol ip prio 1 tcindex classid 1:1 action pass", +- "expExitCode": "0", +- "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol ip tcindex", +- "matchPattern": "handle 0x0001 classid 1:1.*action order [0-9]+: gact action pass", +- "matchCount": "1", +- "teardown": [ +- "$TC qdisc del dev $DEV1 ingress" +- ] +- }, +- { +- "id": "9203", +- "name": "Replace tcindex filter with pass action", +- "category": [ +- "filter", +- "tcindex" +- ], +- "plugins": { +- "requires": "nsPlugin" +- }, +- "setup": [ +- "$TC qdisc add dev $DEV1 ingress", +- "$TC filter add dev $DEV1 parent ffff: handle 1 protocol ip prio 1 tcindex classid 1:1 action drop" +- ], +- "cmdUnderTest": "$TC filter replace dev $DEV1 parent ffff: handle 1 protocol ip prio 1 tcindex classid 1:1 action pass", +- "expExitCode": "0", +- "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol ip tcindex", +- "matchPattern": "handle 0x0001 classid 1:1.*action order [0-9]+: gact action pass", +- "matchCount": "1", +- "teardown": [ +- "$TC qdisc del dev $DEV1 ingress" +- ] +- }, +- { +- "id": "7957", +- "name": "Delete tcindex filter with drop action", +- "category": [ +- "filter", +- "tcindex" +- ], +- "plugins": { +- "requires": "nsPlugin" +- }, +- "setup": [ +- "$TC qdisc add dev $DEV1 ingress", +- "$TC filter add dev $DEV1 parent ffff: handle 1 protocol ip prio 1 tcindex classid 1:1 action drop" +- ], +- "cmdUnderTest": "$TC filter del dev $DEV1 parent ffff: handle 1 protocol ip prio 1 tcindex classid 1:1 action drop", +- "expExitCode": "0", +- "verifyCmd": "$TC filter get dev $DEV1 parent ffff: handle 1 prio 1 protocol ip tcindex", +- "matchPattern": "handle 0x0001 classid 1:1.*action order [0-9]+: gact action drop", +- "matchCount": "0", +- "teardown": [ +- "$TC qdisc del dev $DEV1 ingress" +- ] +- } +-] +-- +cgit diff --git a/tests/cases/CVE-2023-2156.patch b/tests/cases/CVE-2023-2156.patch new file mode 100644 index 0000000..dee227d --- /dev/null +++ b/tests/cases/CVE-2023-2156.patch @@ -0,0 +1,189 @@ +From a2f4c143d76b1a47c91ef9bc46907116b111da0b Mon Sep 17 00:00:00 2001 +From: Kuniyuki Iwashima +Date: Mon, 5 Jun 2023 11:06:17 -0700 +Subject: ipv6: rpl: Fix Route of Death. + +A remote DoS vulnerability of RPL Source Routing is assigned CVE-2023-2156. + +The Source Routing Header (SRH) has the following format: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Next Header | Hdr Ext Len | Routing Type | Segments Left | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | CmprI | CmprE | Pad | Reserved | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + . . + . Addresses[1..n] . + . . + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +The originator of an SRH places the first hop's IPv6 address in the IPv6 +header's IPv6 Destination Address and the second hop's IPv6 address as +the first address in Addresses[1..n]. + +The CmprI and CmprE fields indicate the number of prefix octets that are +shared with the IPv6 Destination Address. When CmprI or CmprE is not 0, +Addresses[1..n] are compressed as follows: + + 1..n-1 : (16 - CmprI) bytes + n : (16 - CmprE) bytes + +Segments Left indicates the number of route segments remaining. When the +value is not zero, the SRH is forwarded to the next hop. Its address +is extracted from Addresses[n - Segment Left + 1] and swapped with IPv6 +Destination Address. + +When Segment Left is greater than or equal to 2, the size of SRH is not +changed because Addresses[1..n-1] are decompressed and recompressed with +CmprI. + +OTOH, when Segment Left changes from 1 to 0, the new SRH could have a +different size because Addresses[1..n-1] are decompressed with CmprI and +recompressed with CmprE. + +Let's say CmprI is 15 and CmprE is 0. When we receive SRH with Segment +Left >= 2, Addresses[1..n-1] have 1 byte for each, and Addresses[n] has +16 bytes. When Segment Left is 1, Addresses[1..n-1] is decompressed to +16 bytes and not recompressed. Finally, the new SRH will need more room +in the header, and the size is (16 - 1) * (n - 1) bytes. + +Here the max value of n is 255 as Segment Left is u8, so in the worst case, +we have to allocate 3825 bytes in the skb headroom. However, now we only +allocate a small fixed buffer that is IPV6_RPL_SRH_WORST_SWAP_SIZE (16 + 7 +bytes). If the decompressed size overflows the room, skb_push() hits BUG() +below [0]. + +Instead of allocating the fixed buffer for every packet, let's allocate +enough headroom only when we receive SRH with Segment Left 1. + +[0]: +skbuff: skb_under_panic: text:ffffffff81c9f6e2 len:576 put:576 head:ffff8880070b5180 data:ffff8880070b4fb0 tail:0x70 end:0x140 dev:lo +kernel BUG at net/core/skbuff.c:200! +invalid opcode: 0000 [#1] PREEMPT SMP PTI +CPU: 0 PID: 154 Comm: python3 Not tainted 6.4.0-rc4-00190-gc308e9ec0047 #7 +Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014 +RIP: 0010:skb_panic (net/core/skbuff.c:200) +Code: 4f 70 50 8b 87 bc 00 00 00 50 8b 87 b8 00 00 00 50 ff b7 c8 00 00 00 4c 8b 8f c0 00 00 00 48 c7 c7 80 6e 77 82 e8 ad 8b 60 ff <0f> 0b 66 66 2e 0f 1f 84 00 00 00 00 00 90 90 90 90 90 90 90 90 90 +RSP: 0018:ffffc90000003da0 EFLAGS: 00000246 +RAX: 0000000000000085 RBX: ffff8880058a6600 RCX: 0000000000000000 +RDX: 0000000000000000 RSI: ffff88807dc1c540 RDI: ffff88807dc1c540 +RBP: ffffc90000003e48 R08: ffffffff82b392c8 R09: 00000000ffffdfff +R10: ffffffff82a592e0 R11: ffffffff82b092e0 R12: ffff888005b1c800 +R13: ffff8880070b51b8 R14: ffff888005b1ca18 R15: ffff8880070b5190 +FS: 00007f4539f0b740(0000) GS:ffff88807dc00000(0000) knlGS:0000000000000000 +CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +CR2: 000055670baf3000 CR3: 0000000005b0e000 CR4: 00000000007506f0 +PKRU: 55555554 +Call Trace: + + skb_push (net/core/skbuff.c:210) + ipv6_rthdr_rcv (./include/linux/skbuff.h:2880 net/ipv6/exthdrs.c:634 net/ipv6/exthdrs.c:718) + ip6_protocol_deliver_rcu (net/ipv6/ip6_input.c:437 (discriminator 5)) + ip6_input_finish (./include/linux/rcupdate.h:805 net/ipv6/ip6_input.c:483) + __netif_receive_skb_one_core (net/core/dev.c:5494) + process_backlog (./include/linux/rcupdate.h:805 net/core/dev.c:5934) + __napi_poll (net/core/dev.c:6496) + net_rx_action (net/core/dev.c:6565 net/core/dev.c:6696) + __do_softirq (./arch/x86/include/asm/jump_label.h:27 ./include/linux/jump_label.h:207 ./include/trace/events/irq.h:142 kernel/softirq.c:572) + do_softirq (kernel/softirq.c:472 kernel/softirq.c:459) + + + __local_bh_enable_ip (kernel/softirq.c:396) + __dev_queue_xmit (net/core/dev.c:4272) + ip6_finish_output2 (./include/net/neighbour.h:544 net/ipv6/ip6_output.c:134) + rawv6_sendmsg (./include/net/dst.h:458 ./include/linux/netfilter.h:303 net/ipv6/raw.c:656 net/ipv6/raw.c:914) + sock_sendmsg (net/socket.c:724 net/socket.c:747) + __sys_sendto (net/socket.c:2144) + __x64_sys_sendto (net/socket.c:2156 net/socket.c:2152 net/socket.c:2152) + do_syscall_64 (arch/x86/entry/common.c:50 arch/x86/entry/common.c:80) + entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:120) +RIP: 0033:0x7f453a138aea +Code: d8 64 89 02 48 c7 c0 ff ff ff ff eb b8 0f 1f 00 f3 0f 1e fa 41 89 ca 64 8b 04 25 18 00 00 00 85 c0 75 15 b8 2c 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 7e c3 0f 1f 44 00 00 41 54 48 83 ec 30 44 89 +RSP: 002b:00007ffcc212a1c8 EFLAGS: 00000246 ORIG_RAX: 000000000000002c +RAX: ffffffffffffffda RBX: 00007ffcc212a288 RCX: 00007f453a138aea +RDX: 0000000000000060 RSI: 00007f4539084c20 RDI: 0000000000000003 +RBP: 00007f4538308e80 R08: 00007ffcc212a300 R09: 000000000000001c +R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 +R13: ffffffffc4653600 R14: 0000000000000001 R15: 00007f4539712d1b + +Modules linked in: + +Fixes: 8610c7c6e3bd ("net: ipv6: add support for rpl sr exthdr") +Reported-by: Max VA +Closes: https://www.interruptlabs.co.uk/articles/linux-ipv6-route-of-death +Signed-off-by: Kuniyuki Iwashima +Reviewed-by: Eric Dumazet +Link: https://lore.kernel.org/r/20230605180617.67284-1-kuniyu@amazon.com +Signed-off-by: Jakub Kicinski +--- + include/net/rpl.h | 3 --- + net/ipv6/exthdrs.c | 29 +++++++++++------------------ + 2 files changed, 11 insertions(+), 21 deletions(-) + +diff --git a/include/net/rpl.h b/include/net/rpl.h +index 308ef0a05caef..30fe780d1e7c8 100644 +--- a/include/net/rpl.h ++++ b/include/net/rpl.h +@@ -23,9 +23,6 @@ static inline int rpl_init(void) + static inline void rpl_exit(void) {} + #endif + +-/* Worst decompression memory usage ipv6 address (16) + pad 7 */ +-#define IPV6_RPL_SRH_WORST_SWAP_SIZE (sizeof(struct in6_addr) + 7) +- + size_t ipv6_rpl_srh_size(unsigned char n, unsigned char cmpri, + unsigned char cmpre); + +diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c +index a8d961d3a477f..5fa0e37305d9d 100644 +--- a/net/ipv6/exthdrs.c ++++ b/net/ipv6/exthdrs.c +@@ -569,24 +569,6 @@ looped_back: + return -1; + } + +- if (skb_cloned(skb)) { +- if (pskb_expand_head(skb, IPV6_RPL_SRH_WORST_SWAP_SIZE, 0, +- GFP_ATOMIC)) { +- __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), +- IPSTATS_MIB_OUTDISCARDS); +- kfree_skb(skb); +- return -1; +- } +- } else { +- err = skb_cow_head(skb, IPV6_RPL_SRH_WORST_SWAP_SIZE); +- if (unlikely(err)) { +- kfree_skb(skb); +- return -1; +- } +- } +- +- hdr = (struct ipv6_rpl_sr_hdr *)skb_transport_header(skb); +- + if (!pskb_may_pull(skb, ipv6_rpl_srh_size(n, hdr->cmpri, + hdr->cmpre))) { + kfree_skb(skb); +@@ -630,6 +612,17 @@ looped_back: + skb_pull(skb, ((hdr->hdrlen + 1) << 3)); + skb_postpull_rcsum(skb, oldhdr, + sizeof(struct ipv6hdr) + ((hdr->hdrlen + 1) << 3)); ++ if (unlikely(!hdr->segments_left)) { ++ if (pskb_expand_head(skb, sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3), 0, ++ GFP_ATOMIC)) { ++ __IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_OUTDISCARDS); ++ kfree_skb(skb); ++ kfree(buf); ++ return -1; ++ } ++ ++ oldhdr = ipv6_hdr(skb); ++ } + skb_push(skb, ((chdr->hdrlen + 1) << 3) + sizeof(struct ipv6hdr)); + skb_reset_network_header(skb); + skb_mac_header_rebuild(skb); +-- +cgit diff --git a/tests/cases/CVE-2023-2163.patch b/tests/cases/CVE-2023-2163.patch new file mode 100644 index 0000000..c8f4a0c --- /dev/null +++ b/tests/cases/CVE-2023-2163.patch @@ -0,0 +1,419 @@ +From 71b547f561247897a0a14f3082730156c0533fed Mon Sep 17 00:00:00 2001 +From: Daniel Borkmann +Date: Tue, 11 Apr 2023 15:24:13 +0000 +Subject: bpf: Fix incorrect verifier pruning due to missing register precision + taints + +Juan Jose et al reported an issue found via fuzzing where the verifier's +pruning logic prematurely marks a program path as safe. + +Consider the following program: + + 0: (b7) r6 = 1024 + 1: (b7) r7 = 0 + 2: (b7) r8 = 0 + 3: (b7) r9 = -2147483648 + 4: (97) r6 %= 1025 + 5: (05) goto pc+0 + 6: (bd) if r6 <= r9 goto pc+2 + 7: (97) r6 %= 1 + 8: (b7) r9 = 0 + 9: (bd) if r6 <= r9 goto pc+1 + 10: (b7) r6 = 0 + 11: (b7) r0 = 0 + 12: (63) *(u32 *)(r10 -4) = r0 + 13: (18) r4 = 0xffff888103693400 // map_ptr(ks=4,vs=48) + 15: (bf) r1 = r4 + 16: (bf) r2 = r10 + 17: (07) r2 += -4 + 18: (85) call bpf_map_lookup_elem#1 + 19: (55) if r0 != 0x0 goto pc+1 + 20: (95) exit + 21: (77) r6 >>= 10 + 22: (27) r6 *= 8192 + 23: (bf) r1 = r0 + 24: (0f) r0 += r6 + 25: (79) r3 = *(u64 *)(r0 +0) + 26: (7b) *(u64 *)(r1 +0) = r3 + 27: (95) exit + +The verifier treats this as safe, leading to oob read/write access due +to an incorrect verifier conclusion: + + func#0 @0 + 0: R1=ctx(off=0,imm=0) R10=fp0 + 0: (b7) r6 = 1024 ; R6_w=1024 + 1: (b7) r7 = 0 ; R7_w=0 + 2: (b7) r8 = 0 ; R8_w=0 + 3: (b7) r9 = -2147483648 ; R9_w=-2147483648 + 4: (97) r6 %= 1025 ; R6_w=scalar() + 5: (05) goto pc+0 + 6: (bd) if r6 <= r9 goto pc+2 ; R6_w=scalar(umin=18446744071562067969,var_off=(0xffffffff00000000; 0xffffffff)) R9_w=-2147483648 + 7: (97) r6 %= 1 ; R6_w=scalar() + 8: (b7) r9 = 0 ; R9=0 + 9: (bd) if r6 <= r9 goto pc+1 ; R6=scalar(umin=1) R9=0 + 10: (b7) r6 = 0 ; R6_w=0 + 11: (b7) r0 = 0 ; R0_w=0 + 12: (63) *(u32 *)(r10 -4) = r0 + last_idx 12 first_idx 9 + regs=1 stack=0 before 11: (b7) r0 = 0 + 13: R0_w=0 R10=fp0 fp-8=0000???? + 13: (18) r4 = 0xffff8ad3886c2a00 ; R4_w=map_ptr(off=0,ks=4,vs=48,imm=0) + 15: (bf) r1 = r4 ; R1_w=map_ptr(off=0,ks=4,vs=48,imm=0) R4_w=map_ptr(off=0,ks=4,vs=48,imm=0) + 16: (bf) r2 = r10 ; R2_w=fp0 R10=fp0 + 17: (07) r2 += -4 ; R2_w=fp-4 + 18: (85) call bpf_map_lookup_elem#1 ; R0=map_value_or_null(id=1,off=0,ks=4,vs=48,imm=0) + 19: (55) if r0 != 0x0 goto pc+1 ; R0=0 + 20: (95) exit + + from 19 to 21: R0=map_value(off=0,ks=4,vs=48,imm=0) R6=0 R7=0 R8=0 R9=0 R10=fp0 fp-8=mmmm???? + 21: (77) r6 >>= 10 ; R6_w=0 + 22: (27) r6 *= 8192 ; R6_w=0 + 23: (bf) r1 = r0 ; R0=map_value(off=0,ks=4,vs=48,imm=0) R1_w=map_value(off=0,ks=4,vs=48,imm=0) + 24: (0f) r0 += r6 + last_idx 24 first_idx 19 + regs=40 stack=0 before 23: (bf) r1 = r0 + regs=40 stack=0 before 22: (27) r6 *= 8192 + regs=40 stack=0 before 21: (77) r6 >>= 10 + regs=40 stack=0 before 19: (55) if r0 != 0x0 goto pc+1 + parent didn't have regs=40 stack=0 marks: R0_rw=map_value_or_null(id=1,off=0,ks=4,vs=48,imm=0) R6_rw=P0 R7=0 R8=0 R9=0 R10=fp0 fp-8=mmmm???? + last_idx 18 first_idx 9 + regs=40 stack=0 before 18: (85) call bpf_map_lookup_elem#1 + regs=40 stack=0 before 17: (07) r2 += -4 + regs=40 stack=0 before 16: (bf) r2 = r10 + regs=40 stack=0 before 15: (bf) r1 = r4 + regs=40 stack=0 before 13: (18) r4 = 0xffff8ad3886c2a00 + regs=40 stack=0 before 12: (63) *(u32 *)(r10 -4) = r0 + regs=40 stack=0 before 11: (b7) r0 = 0 + regs=40 stack=0 before 10: (b7) r6 = 0 + 25: (79) r3 = *(u64 *)(r0 +0) ; R0_w=map_value(off=0,ks=4,vs=48,imm=0) R3_w=scalar() + 26: (7b) *(u64 *)(r1 +0) = r3 ; R1_w=map_value(off=0,ks=4,vs=48,imm=0) R3_w=scalar() + 27: (95) exit + + from 9 to 11: R1=ctx(off=0,imm=0) R6=0 R7=0 R8=0 R9=0 R10=fp0 + 11: (b7) r0 = 0 ; R0_w=0 + 12: (63) *(u32 *)(r10 -4) = r0 + last_idx 12 first_idx 11 + regs=1 stack=0 before 11: (b7) r0 = 0 + 13: R0_w=0 R10=fp0 fp-8=0000???? + 13: (18) r4 = 0xffff8ad3886c2a00 ; R4_w=map_ptr(off=0,ks=4,vs=48,imm=0) + 15: (bf) r1 = r4 ; R1_w=map_ptr(off=0,ks=4,vs=48,imm=0) R4_w=map_ptr(off=0,ks=4,vs=48,imm=0) + 16: (bf) r2 = r10 ; R2_w=fp0 R10=fp0 + 17: (07) r2 += -4 ; R2_w=fp-4 + 18: (85) call bpf_map_lookup_elem#1 + frame 0: propagating r6 + last_idx 19 first_idx 11 + regs=40 stack=0 before 18: (85) call bpf_map_lookup_elem#1 + regs=40 stack=0 before 17: (07) r2 += -4 + regs=40 stack=0 before 16: (bf) r2 = r10 + regs=40 stack=0 before 15: (bf) r1 = r4 + regs=40 stack=0 before 13: (18) r4 = 0xffff8ad3886c2a00 + regs=40 stack=0 before 12: (63) *(u32 *)(r10 -4) = r0 + regs=40 stack=0 before 11: (b7) r0 = 0 + parent didn't have regs=40 stack=0 marks: R1=ctx(off=0,imm=0) R6_r=P0 R7=0 R8=0 R9=0 R10=fp0 + last_idx 9 first_idx 9 + regs=40 stack=0 before 9: (bd) if r6 <= r9 goto pc+1 + parent didn't have regs=40 stack=0 marks: R1=ctx(off=0,imm=0) R6_rw=Pscalar() R7_w=0 R8_w=0 R9_rw=0 R10=fp0 + last_idx 8 first_idx 0 + regs=40 stack=0 before 8: (b7) r9 = 0 + regs=40 stack=0 before 7: (97) r6 %= 1 + regs=40 stack=0 before 6: (bd) if r6 <= r9 goto pc+2 + regs=40 stack=0 before 5: (05) goto pc+0 + regs=40 stack=0 before 4: (97) r6 %= 1025 + regs=40 stack=0 before 3: (b7) r9 = -2147483648 + regs=40 stack=0 before 2: (b7) r8 = 0 + regs=40 stack=0 before 1: (b7) r7 = 0 + regs=40 stack=0 before 0: (b7) r6 = 1024 + 19: safe + frame 0: propagating r6 + last_idx 9 first_idx 0 + regs=40 stack=0 before 6: (bd) if r6 <= r9 goto pc+2 + regs=40 stack=0 before 5: (05) goto pc+0 + regs=40 stack=0 before 4: (97) r6 %= 1025 + regs=40 stack=0 before 3: (b7) r9 = -2147483648 + regs=40 stack=0 before 2: (b7) r8 = 0 + regs=40 stack=0 before 1: (b7) r7 = 0 + regs=40 stack=0 before 0: (b7) r6 = 1024 + + from 6 to 9: safe + verification time 110 usec + stack depth 4 + processed 36 insns (limit 1000000) max_states_per_insn 0 total_states 3 peak_states 3 mark_read 2 + +The verifier considers this program as safe by mistakenly pruning unsafe +code paths. In the above func#0, code lines 0-10 are of interest. In line +0-3 registers r6 to r9 are initialized with known scalar values. In line 4 +the register r6 is reset to an unknown scalar given the verifier does not +track modulo operations. Due to this, the verifier can also not determine +precisely which branches in line 6 and 9 are taken, therefore it needs to +explore them both. + +As can be seen, the verifier starts with exploring the false/fall-through +paths first. The 'from 19 to 21' path has both r6=0 and r9=0 and the pointer +arithmetic on r0 += r6 is therefore considered safe. Given the arithmetic, +r6 is correctly marked for precision tracking where backtracking kicks in +where it walks back the current path all the way where r6 was set to 0 in +the fall-through branch. + +Next, the pruning logics pops the path 'from 9 to 11' from the stack. Also +here, the state of the registers is the same, that is, r6=0 and r9=0, so +that at line 19 the path can be pruned as it is considered safe. It is +interesting to note that the conditional in line 9 turned r6 into a more +precise state, that is, in the fall-through path at the beginning of line +10, it is R6=scalar(umin=1), and in the branch-taken path (which is analyzed +here) at the beginning of line 11, r6 turned into a known const r6=0 as +r9=0 prior to that and therefore (unsigned) r6 <= 0 concludes that r6 must +be 0 (**): + + [...] ; R6_w=scalar() + 9: (bd) if r6 <= r9 goto pc+1 ; R6=scalar(umin=1) R9=0 + [...] + + from 9 to 11: R1=ctx(off=0,imm=0) R6=0 R7=0 R8=0 R9=0 R10=fp0 + [...] + +The next path is 'from 6 to 9'. The verifier considers the old and current +state equivalent, and therefore prunes the search incorrectly. Looking into +the two states which are being compared by the pruning logic at line 9, the +old state consists of R6_rwD=Pscalar() R9_rwD=0 R10=fp0 and the new state +consists of R1=ctx(off=0,imm=0) R6_w=scalar(umax=18446744071562067968) +R7_w=0 R8_w=0 R9_w=-2147483648 R10=fp0. While r6 had the reg->precise flag +correctly set in the old state, r9 did not. Both r6'es are considered as +equivalent given the old one is a superset of the current, more precise one, +however, r9's actual values (0 vs 0x80000000) mismatch. Given the old r9 +did not have reg->precise flag set, the verifier does not consider the +register as contributing to the precision state of r6, and therefore it +considered both r9 states as equivalent. However, for this specific pruned +path (which is also the actual path taken at runtime), register r6 will be +0x400 and r9 0x80000000 when reaching line 21, thus oob-accessing the map. + +The purpose of precision tracking is to initially mark registers (including +spilled ones) as imprecise to help verifier's pruning logic finding equivalent +states it can then prune if they don't contribute to the program's safety +aspects. For example, if registers are used for pointer arithmetic or to pass +constant length to a helper, then the verifier sets reg->precise flag and +backtracks the BPF program instruction sequence and chain of verifier states +to ensure that the given register or stack slot including their dependencies +are marked as precisely tracked scalar. This also includes any other registers +and slots that contribute to a tracked state of given registers/stack slot. +This backtracking relies on recorded jmp_history and is able to traverse +entire chain of parent states. This process ends only when all the necessary +registers/slots and their transitive dependencies are marked as precise. + +The backtrack_insn() is called from the current instruction up to the first +instruction, and its purpose is to compute a bitmask of registers and stack +slots that need precision tracking in the parent's verifier state. For example, +if a current instruction is r6 = r7, then r6 needs precision after this +instruction and r7 needs precision before this instruction, that is, in the +parent state. Hence for the latter r7 is marked and r6 unmarked. + +For the class of jmp/jmp32 instructions, backtrack_insn() today only looks +at call and exit instructions and for all other conditionals the masks +remain as-is. However, in the given situation register r6 has a dependency +on r9 (as described above in **), so also that one needs to be marked for +precision tracking. In other words, if an imprecise register influences a +precise one, then the imprecise register should also be marked precise. +Meaning, in the parent state both dest and src register need to be tracked +for precision and therefore the marking must be more conservative by setting +reg->precise flag for both. The precision propagation needs to cover both +for the conditional: if the src reg was marked but not the dst reg and vice +versa. + +After the fix the program is correctly rejected: + + func#0 @0 + 0: R1=ctx(off=0,imm=0) R10=fp0 + 0: (b7) r6 = 1024 ; R6_w=1024 + 1: (b7) r7 = 0 ; R7_w=0 + 2: (b7) r8 = 0 ; R8_w=0 + 3: (b7) r9 = -2147483648 ; R9_w=-2147483648 + 4: (97) r6 %= 1025 ; R6_w=scalar() + 5: (05) goto pc+0 + 6: (bd) if r6 <= r9 goto pc+2 ; R6_w=scalar(umin=18446744071562067969,var_off=(0xffffffff80000000; 0x7fffffff),u32_min=-2147483648) R9_w=-2147483648 + 7: (97) r6 %= 1 ; R6_w=scalar() + 8: (b7) r9 = 0 ; R9=0 + 9: (bd) if r6 <= r9 goto pc+1 ; R6=scalar(umin=1) R9=0 + 10: (b7) r6 = 0 ; R6_w=0 + 11: (b7) r0 = 0 ; R0_w=0 + 12: (63) *(u32 *)(r10 -4) = r0 + last_idx 12 first_idx 9 + regs=1 stack=0 before 11: (b7) r0 = 0 + 13: R0_w=0 R10=fp0 fp-8=0000???? + 13: (18) r4 = 0xffff9290dc5bfe00 ; R4_w=map_ptr(off=0,ks=4,vs=48,imm=0) + 15: (bf) r1 = r4 ; R1_w=map_ptr(off=0,ks=4,vs=48,imm=0) R4_w=map_ptr(off=0,ks=4,vs=48,imm=0) + 16: (bf) r2 = r10 ; R2_w=fp0 R10=fp0 + 17: (07) r2 += -4 ; R2_w=fp-4 + 18: (85) call bpf_map_lookup_elem#1 ; R0=map_value_or_null(id=1,off=0,ks=4,vs=48,imm=0) + 19: (55) if r0 != 0x0 goto pc+1 ; R0=0 + 20: (95) exit + + from 19 to 21: R0=map_value(off=0,ks=4,vs=48,imm=0) R6=0 R7=0 R8=0 R9=0 R10=fp0 fp-8=mmmm???? + 21: (77) r6 >>= 10 ; R6_w=0 + 22: (27) r6 *= 8192 ; R6_w=0 + 23: (bf) r1 = r0 ; R0=map_value(off=0,ks=4,vs=48,imm=0) R1_w=map_value(off=0,ks=4,vs=48,imm=0) + 24: (0f) r0 += r6 + last_idx 24 first_idx 19 + regs=40 stack=0 before 23: (bf) r1 = r0 + regs=40 stack=0 before 22: (27) r6 *= 8192 + regs=40 stack=0 before 21: (77) r6 >>= 10 + regs=40 stack=0 before 19: (55) if r0 != 0x0 goto pc+1 + parent didn't have regs=40 stack=0 marks: R0_rw=map_value_or_null(id=1,off=0,ks=4,vs=48,imm=0) R6_rw=P0 R7=0 R8=0 R9=0 R10=fp0 fp-8=mmmm???? + last_idx 18 first_idx 9 + regs=40 stack=0 before 18: (85) call bpf_map_lookup_elem#1 + regs=40 stack=0 before 17: (07) r2 += -4 + regs=40 stack=0 before 16: (bf) r2 = r10 + regs=40 stack=0 before 15: (bf) r1 = r4 + regs=40 stack=0 before 13: (18) r4 = 0xffff9290dc5bfe00 + regs=40 stack=0 before 12: (63) *(u32 *)(r10 -4) = r0 + regs=40 stack=0 before 11: (b7) r0 = 0 + regs=40 stack=0 before 10: (b7) r6 = 0 + 25: (79) r3 = *(u64 *)(r0 +0) ; R0_w=map_value(off=0,ks=4,vs=48,imm=0) R3_w=scalar() + 26: (7b) *(u64 *)(r1 +0) = r3 ; R1_w=map_value(off=0,ks=4,vs=48,imm=0) R3_w=scalar() + 27: (95) exit + + from 9 to 11: R1=ctx(off=0,imm=0) R6=0 R7=0 R8=0 R9=0 R10=fp0 + 11: (b7) r0 = 0 ; R0_w=0 + 12: (63) *(u32 *)(r10 -4) = r0 + last_idx 12 first_idx 11 + regs=1 stack=0 before 11: (b7) r0 = 0 + 13: R0_w=0 R10=fp0 fp-8=0000???? + 13: (18) r4 = 0xffff9290dc5bfe00 ; R4_w=map_ptr(off=0,ks=4,vs=48,imm=0) + 15: (bf) r1 = r4 ; R1_w=map_ptr(off=0,ks=4,vs=48,imm=0) R4_w=map_ptr(off=0,ks=4,vs=48,imm=0) + 16: (bf) r2 = r10 ; R2_w=fp0 R10=fp0 + 17: (07) r2 += -4 ; R2_w=fp-4 + 18: (85) call bpf_map_lookup_elem#1 + frame 0: propagating r6 + last_idx 19 first_idx 11 + regs=40 stack=0 before 18: (85) call bpf_map_lookup_elem#1 + regs=40 stack=0 before 17: (07) r2 += -4 + regs=40 stack=0 before 16: (bf) r2 = r10 + regs=40 stack=0 before 15: (bf) r1 = r4 + regs=40 stack=0 before 13: (18) r4 = 0xffff9290dc5bfe00 + regs=40 stack=0 before 12: (63) *(u32 *)(r10 -4) = r0 + regs=40 stack=0 before 11: (b7) r0 = 0 + parent didn't have regs=40 stack=0 marks: R1=ctx(off=0,imm=0) R6_r=P0 R7=0 R8=0 R9=0 R10=fp0 + last_idx 9 first_idx 9 + regs=40 stack=0 before 9: (bd) if r6 <= r9 goto pc+1 + parent didn't have regs=240 stack=0 marks: R1=ctx(off=0,imm=0) R6_rw=Pscalar() R7_w=0 R8_w=0 R9_rw=P0 R10=fp0 + last_idx 8 first_idx 0 + regs=240 stack=0 before 8: (b7) r9 = 0 + regs=40 stack=0 before 7: (97) r6 %= 1 + regs=40 stack=0 before 6: (bd) if r6 <= r9 goto pc+2 + regs=240 stack=0 before 5: (05) goto pc+0 + regs=240 stack=0 before 4: (97) r6 %= 1025 + regs=240 stack=0 before 3: (b7) r9 = -2147483648 + regs=40 stack=0 before 2: (b7) r8 = 0 + regs=40 stack=0 before 1: (b7) r7 = 0 + regs=40 stack=0 before 0: (b7) r6 = 1024 + 19: safe + + from 6 to 9: R1=ctx(off=0,imm=0) R6_w=scalar(umax=18446744071562067968) R7_w=0 R8_w=0 R9_w=-2147483648 R10=fp0 + 9: (bd) if r6 <= r9 goto pc+1 + last_idx 9 first_idx 0 + regs=40 stack=0 before 6: (bd) if r6 <= r9 goto pc+2 + regs=240 stack=0 before 5: (05) goto pc+0 + regs=240 stack=0 before 4: (97) r6 %= 1025 + regs=240 stack=0 before 3: (b7) r9 = -2147483648 + regs=40 stack=0 before 2: (b7) r8 = 0 + regs=40 stack=0 before 1: (b7) r7 = 0 + regs=40 stack=0 before 0: (b7) r6 = 1024 + last_idx 9 first_idx 0 + regs=200 stack=0 before 6: (bd) if r6 <= r9 goto pc+2 + regs=240 stack=0 before 5: (05) goto pc+0 + regs=240 stack=0 before 4: (97) r6 %= 1025 + regs=240 stack=0 before 3: (b7) r9 = -2147483648 + regs=40 stack=0 before 2: (b7) r8 = 0 + regs=40 stack=0 before 1: (b7) r7 = 0 + regs=40 stack=0 before 0: (b7) r6 = 1024 + 11: R6=scalar(umax=18446744071562067968) R9=-2147483648 + 11: (b7) r0 = 0 ; R0_w=0 + 12: (63) *(u32 *)(r10 -4) = r0 + last_idx 12 first_idx 11 + regs=1 stack=0 before 11: (b7) r0 = 0 + 13: R0_w=0 R10=fp0 fp-8=0000???? + 13: (18) r4 = 0xffff9290dc5bfe00 ; R4_w=map_ptr(off=0,ks=4,vs=48,imm=0) + 15: (bf) r1 = r4 ; R1_w=map_ptr(off=0,ks=4,vs=48,imm=0) R4_w=map_ptr(off=0,ks=4,vs=48,imm=0) + 16: (bf) r2 = r10 ; R2_w=fp0 R10=fp0 + 17: (07) r2 += -4 ; R2_w=fp-4 + 18: (85) call bpf_map_lookup_elem#1 ; R0_w=map_value_or_null(id=3,off=0,ks=4,vs=48,imm=0) + 19: (55) if r0 != 0x0 goto pc+1 ; R0_w=0 + 20: (95) exit + + from 19 to 21: R0=map_value(off=0,ks=4,vs=48,imm=0) R6=scalar(umax=18446744071562067968) R7=0 R8=0 R9=-2147483648 R10=fp0 fp-8=mmmm???? + 21: (77) r6 >>= 10 ; R6_w=scalar(umax=18014398507384832,var_off=(0x0; 0x3fffffffffffff)) + 22: (27) r6 *= 8192 ; R6_w=scalar(smax=9223372036854767616,umax=18446744073709543424,var_off=(0x0; 0xffffffffffffe000),s32_max=2147475456,u32_max=-8192) + 23: (bf) r1 = r0 ; R0=map_value(off=0,ks=4,vs=48,imm=0) R1_w=map_value(off=0,ks=4,vs=48,imm=0) + 24: (0f) r0 += r6 + last_idx 24 first_idx 21 + regs=40 stack=0 before 23: (bf) r1 = r0 + regs=40 stack=0 before 22: (27) r6 *= 8192 + regs=40 stack=0 before 21: (77) r6 >>= 10 + parent didn't have regs=40 stack=0 marks: R0_rw=map_value(off=0,ks=4,vs=48,imm=0) R6_r=Pscalar(umax=18446744071562067968) R7=0 R8=0 R9=-2147483648 R10=fp0 fp-8=mmmm???? + last_idx 19 first_idx 11 + regs=40 stack=0 before 19: (55) if r0 != 0x0 goto pc+1 + regs=40 stack=0 before 18: (85) call bpf_map_lookup_elem#1 + regs=40 stack=0 before 17: (07) r2 += -4 + regs=40 stack=0 before 16: (bf) r2 = r10 + regs=40 stack=0 before 15: (bf) r1 = r4 + regs=40 stack=0 before 13: (18) r4 = 0xffff9290dc5bfe00 + regs=40 stack=0 before 12: (63) *(u32 *)(r10 -4) = r0 + regs=40 stack=0 before 11: (b7) r0 = 0 + parent didn't have regs=40 stack=0 marks: R1=ctx(off=0,imm=0) R6_rw=Pscalar(umax=18446744071562067968) R7_w=0 R8_w=0 R9_w=-2147483648 R10=fp0 + last_idx 9 first_idx 0 + regs=40 stack=0 before 9: (bd) if r6 <= r9 goto pc+1 + regs=240 stack=0 before 6: (bd) if r6 <= r9 goto pc+2 + regs=240 stack=0 before 5: (05) goto pc+0 + regs=240 stack=0 before 4: (97) r6 %= 1025 + regs=240 stack=0 before 3: (b7) r9 = -2147483648 + regs=40 stack=0 before 2: (b7) r8 = 0 + regs=40 stack=0 before 1: (b7) r7 = 0 + regs=40 stack=0 before 0: (b7) r6 = 1024 + math between map_value pointer and register with unbounded min value is not allowed + verification time 886 usec + stack depth 4 + processed 49 insns (limit 1000000) max_states_per_insn 1 total_states 5 peak_states 5 mark_read 2 + +Fixes: b5dc0163d8fd ("bpf: precise scalar_value tracking") +Reported-by: Juan Jose Lopez Jaimez +Reported-by: Meador Inge +Reported-by: Simon Scannell +Reported-by: Nenad Stojanovski +Signed-off-by: Daniel Borkmann +Co-developed-by: Andrii Nakryiko +Signed-off-by: Andrii Nakryiko +Reviewed-by: John Fastabend +Reviewed-by: Juan Jose Lopez Jaimez +Reviewed-by: Meador Inge +Reviewed-by: Simon Scannell +--- + kernel/bpf/verifier.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c +index d517d13878cfe7..767e8930b0bd5d 100644 +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -2967,6 +2967,21 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, + } + } else if (opcode == BPF_EXIT) { + return -ENOTSUPP; ++ } else if (BPF_SRC(insn->code) == BPF_X) { ++ if (!(*reg_mask & (dreg | sreg))) ++ return 0; ++ /* dreg sreg ++ * Both dreg and sreg need precision before ++ * this insn. If only sreg was marked precise ++ * before it would be equally necessary to ++ * propagate it to dreg. ++ */ ++ *reg_mask |= (sreg | dreg); ++ /* else dreg K ++ * Only dreg still needs precision before ++ * this insn, so for the K-based conditional ++ * there is nothing new to be marked. ++ */ + } + } else if (class == BPF_LD) { + if (!(*reg_mask & dreg)) +-- +cgit 1.2.3-korg diff --git a/tests/cases/CVE-2023-31248.patch b/tests/cases/CVE-2023-31248.patch new file mode 100644 index 0000000..138c959 --- /dev/null +++ b/tests/cases/CVE-2023-31248.patch @@ -0,0 +1,120 @@ +From 515ad530795c118f012539ed76d02bacfd426d89 Mon Sep 17 00:00:00 2001 +From: Thadeu Lima de Souza Cascardo +Date: Wed, 5 Jul 2023 09:12:55 -0300 +Subject: netfilter: nf_tables: do not ignore genmask when looking up chain by + id + +When adding a rule to a chain referring to its ID, if that chain had been +deleted on the same batch, the rule might end up referring to a deleted +chain. + +This will lead to a WARNING like following: + +[ 33.098431] ------------[ cut here ]------------ +[ 33.098678] WARNING: CPU: 5 PID: 69 at net/netfilter/nf_tables_api.c:2037 nf_tables_chain_destroy+0x23d/0x260 +[ 33.099217] Modules linked in: +[ 33.099388] CPU: 5 PID: 69 Comm: kworker/5:1 Not tainted 6.4.0+ #409 +[ 33.099726] Workqueue: events nf_tables_trans_destroy_work +[ 33.100018] RIP: 0010:nf_tables_chain_destroy+0x23d/0x260 +[ 33.100306] Code: 8b 7c 24 68 e8 64 9c ed fe 4c 89 e7 e8 5c 9c ed fe 48 83 c4 08 5b 41 5c 41 5d 41 5e 41 5f 5d 31 c0 89 c6 89 c7 c3 cc cc cc cc <0f> 0b 48 83 c4 08 5b 41 5c 41 5d 41 5e 41 5f 5d 31 c0 89 c6 89 c7 +[ 33.101271] RSP: 0018:ffffc900004ffc48 EFLAGS: 00010202 +[ 33.101546] RAX: 0000000000000001 RBX: ffff888006fc0a28 RCX: 0000000000000000 +[ 33.101920] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000 +[ 33.102649] RBP: ffffc900004ffc78 R08: 0000000000000000 R09: 0000000000000000 +[ 33.103018] R10: 0000000000000000 R11: 0000000000000000 R12: ffff8880135ef500 +[ 33.103385] R13: 0000000000000000 R14: dead000000000122 R15: ffff888006fc0a10 +[ 33.103762] FS: 0000000000000000(0000) GS:ffff888024c80000(0000) knlGS:0000000000000000 +[ 33.104184] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +[ 33.104493] CR2: 00007fe863b56a50 CR3: 00000000124b0001 CR4: 0000000000770ee0 +[ 33.104872] PKRU: 55555554 +[ 33.104999] Call Trace: +[ 33.105113] +[ 33.105214] ? show_regs+0x72/0x90 +[ 33.105371] ? __warn+0xa5/0x210 +[ 33.105520] ? nf_tables_chain_destroy+0x23d/0x260 +[ 33.105732] ? report_bug+0x1f2/0x200 +[ 33.105902] ? handle_bug+0x46/0x90 +[ 33.106546] ? exc_invalid_op+0x19/0x50 +[ 33.106762] ? asm_exc_invalid_op+0x1b/0x20 +[ 33.106995] ? nf_tables_chain_destroy+0x23d/0x260 +[ 33.107249] ? nf_tables_chain_destroy+0x30/0x260 +[ 33.107506] nf_tables_trans_destroy_work+0x669/0x680 +[ 33.107782] ? mark_held_locks+0x28/0xa0 +[ 33.107996] ? __pfx_nf_tables_trans_destroy_work+0x10/0x10 +[ 33.108294] ? _raw_spin_unlock_irq+0x28/0x70 +[ 33.108538] process_one_work+0x68c/0xb70 +[ 33.108755] ? lock_acquire+0x17f/0x420 +[ 33.108977] ? __pfx_process_one_work+0x10/0x10 +[ 33.109218] ? do_raw_spin_lock+0x128/0x1d0 +[ 33.109435] ? _raw_spin_lock_irq+0x71/0x80 +[ 33.109634] worker_thread+0x2bd/0x700 +[ 33.109817] ? __pfx_worker_thread+0x10/0x10 +[ 33.110254] kthread+0x18b/0x1d0 +[ 33.110410] ? __pfx_kthread+0x10/0x10 +[ 33.110581] ret_from_fork+0x29/0x50 +[ 33.110757] +[ 33.110866] irq event stamp: 1651 +[ 33.111017] hardirqs last enabled at (1659): [] __up_console_sem+0x79/0xa0 +[ 33.111379] hardirqs last disabled at (1666): [] __up_console_sem+0x5e/0xa0 +[ 33.111740] softirqs last enabled at (1616): [] __irq_exit_rcu+0x9e/0xe0 +[ 33.112094] softirqs last disabled at (1367): [] __irq_exit_rcu+0x9e/0xe0 +[ 33.112453] ---[ end trace 0000000000000000 ]--- + +This is due to the nft_chain_lookup_byid ignoring the genmask. After this +change, adding the new rule will fail as it will not find the chain. + +Fixes: 837830a4b439 ("netfilter: nf_tables: add NFTA_RULE_CHAIN_ID attribute") +Cc: stable@vger.kernel.org +Reported-by: Mingi Cho of Theori working with ZDI +Signed-off-by: Thadeu Lima de Souza Cascardo +Reviewed-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +--- + net/netfilter/nf_tables_api.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c +index 86b3c4de7f40d..237f739da3ca7 100644 +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -2699,7 +2699,7 @@ err_hooks: + + static struct nft_chain *nft_chain_lookup_byid(const struct net *net, + const struct nft_table *table, +- const struct nlattr *nla) ++ const struct nlattr *nla, u8 genmask) + { + struct nftables_pernet *nft_net = nft_pernet(net); + u32 id = ntohl(nla_get_be32(nla)); +@@ -2710,7 +2710,8 @@ static struct nft_chain *nft_chain_lookup_byid(const struct net *net, + + if (trans->msg_type == NFT_MSG_NEWCHAIN && + chain->table == table && +- id == nft_trans_chain_id(trans)) ++ id == nft_trans_chain_id(trans) && ++ nft_active_genmask(chain, genmask)) + return chain; + } + return ERR_PTR(-ENOENT); +@@ -3814,7 +3815,8 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info, + return -EOPNOTSUPP; + + } else if (nla[NFTA_RULE_CHAIN_ID]) { +- chain = nft_chain_lookup_byid(net, table, nla[NFTA_RULE_CHAIN_ID]); ++ chain = nft_chain_lookup_byid(net, table, nla[NFTA_RULE_CHAIN_ID], ++ genmask); + if (IS_ERR(chain)) { + NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN_ID]); + return PTR_ERR(chain); +@@ -10540,7 +10542,8 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, + genmask); + } else if (tb[NFTA_VERDICT_CHAIN_ID]) { + chain = nft_chain_lookup_byid(ctx->net, ctx->table, +- tb[NFTA_VERDICT_CHAIN_ID]); ++ tb[NFTA_VERDICT_CHAIN_ID], ++ genmask); + if (IS_ERR(chain)) + return PTR_ERR(chain); + } else { +-- +cgit diff --git a/tests/cases/CVE-2023-31436.patch b/tests/cases/CVE-2023-31436.patch new file mode 100644 index 0000000..121d761 --- /dev/null +++ b/tests/cases/CVE-2023-31436.patch @@ -0,0 +1,128 @@ +From 3037933448f60f9acb705997eae62013ecb81e0d Mon Sep 17 00:00:00 2001 +From: Gwangun Jung +Date: Thu, 13 Apr 2023 19:35:54 +0900 +Subject: net: sched: sch_qfq: prevent slab-out-of-bounds in qfq_activate_agg + +If the TCA_QFQ_LMAX value is not offered through nlattr, lmax is determined by the MTU value of the network device. +The MTU of the loopback device can be set up to 2^31-1. +As a result, it is possible to have an lmax value that exceeds QFQ_MIN_LMAX. + +Due to the invalid lmax value, an index is generated that exceeds the QFQ_MAX_INDEX(=24) value, causing out-of-bounds read/write errors. + +The following reports a oob access: + +[ 84.582666] BUG: KASAN: slab-out-of-bounds in qfq_activate_agg.constprop.0 (net/sched/sch_qfq.c:1027 net/sched/sch_qfq.c:1060 net/sched/sch_qfq.c:1313) +[ 84.583267] Read of size 4 at addr ffff88810f676948 by task ping/301 +[ 84.583686] +[ 84.583797] CPU: 3 PID: 301 Comm: ping Not tainted 6.3.0-rc5 #1 +[ 84.584164] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014 +[ 84.584644] Call Trace: +[ 84.584787] +[ 84.584906] dump_stack_lvl (lib/dump_stack.c:107 (discriminator 1)) +[ 84.585108] print_report (mm/kasan/report.c:320 mm/kasan/report.c:430) +[ 84.585570] kasan_report (mm/kasan/report.c:538) +[ 84.585988] qfq_activate_agg.constprop.0 (net/sched/sch_qfq.c:1027 net/sched/sch_qfq.c:1060 net/sched/sch_qfq.c:1313) +[ 84.586599] qfq_enqueue (net/sched/sch_qfq.c:1255) +[ 84.587607] dev_qdisc_enqueue (net/core/dev.c:3776) +[ 84.587749] __dev_queue_xmit (./include/net/sch_generic.h:186 net/core/dev.c:3865 net/core/dev.c:4212) +[ 84.588763] ip_finish_output2 (./include/net/neighbour.h:546 net/ipv4/ip_output.c:228) +[ 84.589460] ip_output (net/ipv4/ip_output.c:430) +[ 84.590132] ip_push_pending_frames (./include/net/dst.h:444 net/ipv4/ip_output.c:126 net/ipv4/ip_output.c:1586 net/ipv4/ip_output.c:1606) +[ 84.590285] raw_sendmsg (net/ipv4/raw.c:649) +[ 84.591960] sock_sendmsg (net/socket.c:724 net/socket.c:747) +[ 84.592084] __sys_sendto (net/socket.c:2142) +[ 84.593306] __x64_sys_sendto (net/socket.c:2150) +[ 84.593779] do_syscall_64 (arch/x86/entry/common.c:50 arch/x86/entry/common.c:80) +[ 84.593902] entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:120) +[ 84.594070] RIP: 0033:0x7fe568032066 +[ 84.594192] Code: 0e 0d 00 f7 d8 64 89 02 48 c7 c0 ff ff ff ff eb b8 0f 1f 00 41 89 ca 64 8b 04 25 18 00 00 00 85 c09[ 84.594796] RSP: 002b:00007ffce388b4e8 EFLAGS: 00000246 ORIG_RAX: 000000000000002c + +Code starting with the faulting instruction +=========================================== +[ 84.595047] RAX: ffffffffffffffda RBX: 00007ffce388cc70 RCX: 00007fe568032066 +[ 84.595281] RDX: 0000000000000040 RSI: 00005605fdad6d10 RDI: 0000000000000003 +[ 84.595515] RBP: 00005605fdad6d10 R08: 00007ffce388eeec R09: 0000000000000010 +[ 84.595749] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000040 +[ 84.595984] R13: 00007ffce388cc30 R14: 00007ffce388b4f0 R15: 0000001d00000001 +[ 84.596218] +[ 84.596295] +[ 84.596351] Allocated by task 291: +[ 84.596467] kasan_save_stack (mm/kasan/common.c:46) +[ 84.596597] kasan_set_track (mm/kasan/common.c:52) +[ 84.596725] __kasan_kmalloc (mm/kasan/common.c:384) +[ 84.596852] __kmalloc_node (./include/linux/kasan.h:196 mm/slab_common.c:967 mm/slab_common.c:974) +[ 84.596979] qdisc_alloc (./include/linux/slab.h:610 ./include/linux/slab.h:731 net/sched/sch_generic.c:938) +[ 84.597100] qdisc_create (net/sched/sch_api.c:1244) +[ 84.597222] tc_modify_qdisc (net/sched/sch_api.c:1680) +[ 84.597357] rtnetlink_rcv_msg (net/core/rtnetlink.c:6174) +[ 84.597495] netlink_rcv_skb (net/netlink/af_netlink.c:2574) +[ 84.597627] netlink_unicast (net/netlink/af_netlink.c:1340 net/netlink/af_netlink.c:1365) +[ 84.597759] netlink_sendmsg (net/netlink/af_netlink.c:1942) +[ 84.597891] sock_sendmsg (net/socket.c:724 net/socket.c:747) +[ 84.598016] ____sys_sendmsg (net/socket.c:2501) +[ 84.598147] ___sys_sendmsg (net/socket.c:2557) +[ 84.598275] __sys_sendmsg (./include/linux/file.h:31 net/socket.c:2586) +[ 84.598399] do_syscall_64 (arch/x86/entry/common.c:50 arch/x86/entry/common.c:80) +[ 84.598520] entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:120) +[ 84.598688] +[ 84.598744] The buggy address belongs to the object at ffff88810f674000 +[ 84.598744] which belongs to the cache kmalloc-8k of size 8192 +[ 84.599135] The buggy address is located 2664 bytes to the right of +[ 84.599135] allocated 7904-byte region [ffff88810f674000, ffff88810f675ee0) +[ 84.599544] +[ 84.599598] The buggy address belongs to the physical page: +[ 84.599777] page:00000000e638567f refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x10f670 +[ 84.600074] head:00000000e638567f order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0 +[ 84.600330] flags: 0x200000000010200(slab|head|node=0|zone=2) +[ 84.600517] raw: 0200000000010200 ffff888100043180 dead000000000122 0000000000000000 +[ 84.600764] raw: 0000000000000000 0000000080020002 00000001ffffffff 0000000000000000 +[ 84.601009] page dumped because: kasan: bad access detected +[ 84.601187] +[ 84.601241] Memory state around the buggy address: +[ 84.601396] ffff88810f676800: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc +[ 84.601620] ffff88810f676880: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc +[ 84.601845] >ffff88810f676900: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc +[ 84.602069] ^ +[ 84.602243] ffff88810f676980: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc +[ 84.602468] ffff88810f676a00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc +[ 84.602693] ================================================================== +[ 84.602924] Disabling lock debugging due to kernel taint + +Fixes: 3015f3d2a3cd ("pkt_sched: enable QFQ to support TSO/GSO") +Reported-by: Gwangun Jung +Signed-off-by: Gwangun Jung +Acked-by: Jamal Hadi Salim +Signed-off-by: David S. Miller +--- + net/sched/sch_qfq.c | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c +index cf5ebe43b3b4eb..02098a02943eb9 100644 +--- a/net/sched/sch_qfq.c ++++ b/net/sched/sch_qfq.c +@@ -421,15 +421,16 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, + } else + weight = 1; + +- if (tb[TCA_QFQ_LMAX]) { ++ if (tb[TCA_QFQ_LMAX]) + lmax = nla_get_u32(tb[TCA_QFQ_LMAX]); +- if (lmax < QFQ_MIN_LMAX || lmax > (1UL << QFQ_MTU_SHIFT)) { +- pr_notice("qfq: invalid max length %u\n", lmax); +- return -EINVAL; +- } +- } else ++ else + lmax = psched_mtu(qdisc_dev(sch)); + ++ if (lmax < QFQ_MIN_LMAX || lmax > (1UL << QFQ_MTU_SHIFT)) { ++ pr_notice("qfq: invalid max length %u\n", lmax); ++ return -EINVAL; ++ } ++ + inv_w = ONE_FP / weight; + weight = ONE_FP / inv_w; + +-- +cgit 1.2.3-korg diff --git a/tests/cases/CVE-2023-32233.patch b/tests/cases/CVE-2023-32233.patch new file mode 100644 index 0000000..788e256 --- /dev/null +++ b/tests/cases/CVE-2023-32233.patch @@ -0,0 +1,120 @@ +From c1592a89942e9678f7d9c8030efa777c0d57edab Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Tue, 2 May 2023 10:25:24 +0200 +Subject: netfilter: nf_tables: deactivate anonymous set from preparation phase + +Toggle deleted anonymous sets as inactive in the next generation, so +users cannot perform any update on it. Clear the generation bitmask +in case the transaction is aborted. + +The following KASAN splat shows a set element deletion for a bound +anonymous set that has been already removed in the same transaction. + +[ 64.921510] ================================================================== +[ 64.923123] BUG: KASAN: wild-memory-access in nf_tables_commit+0xa24/0x1490 [nf_tables] +[ 64.924745] Write of size 8 at addr dead000000000122 by task test/890 +[ 64.927903] CPU: 3 PID: 890 Comm: test Not tainted 6.3.0+ #253 +[ 64.931120] Call Trace: +[ 64.932699] +[ 64.934292] dump_stack_lvl+0x33/0x50 +[ 64.935908] ? nf_tables_commit+0xa24/0x1490 [nf_tables] +[ 64.937551] kasan_report+0xda/0x120 +[ 64.939186] ? nf_tables_commit+0xa24/0x1490 [nf_tables] +[ 64.940814] nf_tables_commit+0xa24/0x1490 [nf_tables] +[ 64.942452] ? __kasan_slab_alloc+0x2d/0x60 +[ 64.944070] ? nf_tables_setelem_notify+0x190/0x190 [nf_tables] +[ 64.945710] ? kasan_set_track+0x21/0x30 +[ 64.947323] nfnetlink_rcv_batch+0x709/0xd90 [nfnetlink] +[ 64.948898] ? nfnetlink_rcv_msg+0x480/0x480 [nfnetlink] + +Signed-off-by: Pablo Neira Ayuso +--- + include/net/netfilter/nf_tables.h | 1 + + net/netfilter/nf_tables_api.c | 12 ++++++++++++ + net/netfilter/nft_dynset.c | 2 +- + net/netfilter/nft_lookup.c | 2 +- + net/netfilter/nft_objref.c | 2 +- + 5 files changed, 16 insertions(+), 3 deletions(-) + +diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h +index 3ed21d2d56590..2e24ea1d744c2 100644 +--- a/include/net/netfilter/nf_tables.h ++++ b/include/net/netfilter/nf_tables.h +@@ -619,6 +619,7 @@ struct nft_set_binding { + }; + + enum nft_trans_phase; ++void nf_tables_activate_set(const struct nft_ctx *ctx, struct nft_set *set); + void nf_tables_deactivate_set(const struct nft_ctx *ctx, struct nft_set *set, + struct nft_set_binding *binding, + enum nft_trans_phase phase); +diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c +index 8b6c61a2196cb..59fb8320ab4d7 100644 +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -5127,12 +5127,24 @@ static void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, + } + } + ++void nf_tables_activate_set(const struct nft_ctx *ctx, struct nft_set *set) ++{ ++ if (nft_set_is_anonymous(set)) ++ nft_clear(ctx->net, set); ++ ++ set->use++; ++} ++EXPORT_SYMBOL_GPL(nf_tables_activate_set); ++ + void nf_tables_deactivate_set(const struct nft_ctx *ctx, struct nft_set *set, + struct nft_set_binding *binding, + enum nft_trans_phase phase) + { + switch (phase) { + case NFT_TRANS_PREPARE: ++ if (nft_set_is_anonymous(set)) ++ nft_deactivate_next(ctx->net, set); ++ + set->use--; + return; + case NFT_TRANS_ABORT: +diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c +index 274579b1696e0..bd19c7aec92ee 100644 +--- a/net/netfilter/nft_dynset.c ++++ b/net/netfilter/nft_dynset.c +@@ -342,7 +342,7 @@ static void nft_dynset_activate(const struct nft_ctx *ctx, + { + struct nft_dynset *priv = nft_expr_priv(expr); + +- priv->set->use++; ++ nf_tables_activate_set(ctx, priv->set); + } + + static void nft_dynset_destroy(const struct nft_ctx *ctx, +diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c +index cecf8ab90e58f..03ef4fdaa460b 100644 +--- a/net/netfilter/nft_lookup.c ++++ b/net/netfilter/nft_lookup.c +@@ -167,7 +167,7 @@ static void nft_lookup_activate(const struct nft_ctx *ctx, + { + struct nft_lookup *priv = nft_expr_priv(expr); + +- priv->set->use++; ++ nf_tables_activate_set(ctx, priv->set); + } + + static void nft_lookup_destroy(const struct nft_ctx *ctx, +diff --git a/net/netfilter/nft_objref.c b/net/netfilter/nft_objref.c +index cb37169608bab..a48dd5b5d45b1 100644 +--- a/net/netfilter/nft_objref.c ++++ b/net/netfilter/nft_objref.c +@@ -185,7 +185,7 @@ static void nft_objref_map_activate(const struct nft_ctx *ctx, + { + struct nft_objref_map *priv = nft_expr_priv(expr); + +- priv->set->use++; ++ nf_tables_activate_set(ctx, priv->set); + } + + static void nft_objref_map_destroy(const struct nft_ctx *ctx, +-- +cgit diff --git a/tests/cases/CVE-2023-3269.patch b/tests/cases/CVE-2023-3269.patch new file mode 100644 index 0000000..142dfa3 --- /dev/null +++ b/tests/cases/CVE-2023-3269.patch @@ -0,0 +1,302 @@ +From c2508ec5a58db67093f4fb8bf89a9a7c53a109e9 Mon Sep 17 00:00:00 2001 +From: Linus Torvalds +Date: Thu, 15 Jun 2023 15:17:36 -0700 +Subject: mm: introduce new 'lock_mm_and_find_vma()' page fault helper + +.. and make x86 use it. + +This basically extracts the existing x86 "find and expand faulting vma" +code, but extends it to also take the mmap lock for writing in case we +actually do need to expand the vma. + +We've historically short-circuited that case, and have some rather ugly +special logic to serialize the stack segment expansion (since we only +hold the mmap lock for reading) that doesn't match the normal VM +locking. + +That slight violation of locking worked well, right up until it didn't: +the maple tree code really does want proper locking even for simple +extension of an existing vma. + +So extract the code for "look up the vma of the fault" from x86, fix it +up to do the necessary write locking, and make it available as a helper +function for other architectures that can use the common helper. + +Note: I say "common helper", but it really only handles the normal +stack-grows-down case. Which is all architectures except for PA-RISC +and IA64. So some rare architectures can't use the helper, but if they +care they'll just need to open-code this logic. + +It's also worth pointing out that this code really would like to have an +optimistic "mmap_upgrade_trylock()" to make it quicker to go from a +read-lock (for the common case) to taking the write lock (for having to +extend the vma) in the normal single-threaded situation where there is +no other locking activity. + +But that _is_ all the very uncommon special case, so while it would be +nice to have such an operation, it probably doesn't matter in reality. +I did put in the skeleton code for such a possible future expansion, +even if it only acts as pseudo-documentation for what we're doing. + +Signed-off-by: Linus Torvalds +--- + arch/x86/Kconfig | 1 + + arch/x86/mm/fault.c | 52 +--------------------- + include/linux/mm.h | 2 + + mm/Kconfig | 4 ++ + mm/memory.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 5 files changed, 130 insertions(+), 50 deletions(-) + +diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig +index 53bab123a8ee4..cb1031018afa5 100644 +--- a/arch/x86/Kconfig ++++ b/arch/x86/Kconfig +@@ -276,6 +276,7 @@ config X86 + select HAVE_GENERIC_VDSO + select HOTPLUG_SMT if SMP + select IRQ_FORCED_THREADING ++ select LOCK_MM_AND_FIND_VMA + select NEED_PER_CPU_EMBED_FIRST_CHUNK + select NEED_PER_CPU_PAGE_FIRST_CHUNK + select NEED_SG_DMA_LENGTH +diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c +index e4399983c50c0..e8711b2cafaf7 100644 +--- a/arch/x86/mm/fault.c ++++ b/arch/x86/mm/fault.c +@@ -880,12 +880,6 @@ __bad_area(struct pt_regs *regs, unsigned long error_code, + __bad_area_nosemaphore(regs, error_code, address, pkey, si_code); + } + +-static noinline void +-bad_area(struct pt_regs *regs, unsigned long error_code, unsigned long address) +-{ +- __bad_area(regs, error_code, address, 0, SEGV_MAPERR); +-} +- + static inline bool bad_area_access_from_pkeys(unsigned long error_code, + struct vm_area_struct *vma) + { +@@ -1366,51 +1360,10 @@ void do_user_addr_fault(struct pt_regs *regs, + lock_mmap: + #endif /* CONFIG_PER_VMA_LOCK */ + +- /* +- * Kernel-mode access to the user address space should only occur +- * on well-defined single instructions listed in the exception +- * tables. But, an erroneous kernel fault occurring outside one of +- * those areas which also holds mmap_lock might deadlock attempting +- * to validate the fault against the address space. +- * +- * Only do the expensive exception table search when we might be at +- * risk of a deadlock. This happens if we +- * 1. Failed to acquire mmap_lock, and +- * 2. The access did not originate in userspace. +- */ +- if (unlikely(!mmap_read_trylock(mm))) { +- if (!user_mode(regs) && !search_exception_tables(regs->ip)) { +- /* +- * Fault from code in kernel from +- * which we do not expect faults. +- */ +- bad_area_nosemaphore(regs, error_code, address); +- return; +- } + retry: +- mmap_read_lock(mm); +- } else { +- /* +- * The above down_read_trylock() might have succeeded in +- * which case we'll have missed the might_sleep() from +- * down_read(): +- */ +- might_sleep(); +- } +- +- vma = find_vma(mm, address); ++ vma = lock_mm_and_find_vma(mm, address, regs); + if (unlikely(!vma)) { +- bad_area(regs, error_code, address); +- return; +- } +- if (likely(vma->vm_start <= address)) +- goto good_area; +- if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) { +- bad_area(regs, error_code, address); +- return; +- } +- if (unlikely(expand_stack(vma, address))) { +- bad_area(regs, error_code, address); ++ bad_area_nosemaphore(regs, error_code, address); + return; + } + +@@ -1418,7 +1371,6 @@ retry: + * Ok, we have a good vm_area for this memory access, so + * we can handle it.. + */ +-good_area: + if (unlikely(access_error(error_code, vma))) { + bad_area_access_error(regs, error_code, address, vma); + return; +diff --git a/include/linux/mm.h b/include/linux/mm.h +index 27ce77080c79c..570cf906fbcc1 100644 +--- a/include/linux/mm.h ++++ b/include/linux/mm.h +@@ -2325,6 +2325,8 @@ void unmap_mapping_pages(struct address_space *mapping, + pgoff_t start, pgoff_t nr, bool even_cows); + void unmap_mapping_range(struct address_space *mapping, + loff_t const holebegin, loff_t const holelen, int even_cows); ++struct vm_area_struct *lock_mm_and_find_vma(struct mm_struct *mm, ++ unsigned long address, struct pt_regs *regs); + #else + static inline vm_fault_t handle_mm_fault(struct vm_area_struct *vma, + unsigned long address, unsigned int flags, +diff --git a/mm/Kconfig b/mm/Kconfig +index 7672a22647b4a..e3454087fd31a 100644 +--- a/mm/Kconfig ++++ b/mm/Kconfig +@@ -1206,6 +1206,10 @@ config PER_VMA_LOCK + This feature allows locking each virtual memory area separately when + handling page faults instead of taking mmap_lock. + ++config LOCK_MM_AND_FIND_VMA ++ bool ++ depends on !STACK_GROWSUP ++ + source "mm/damon/Kconfig" + + endmenu +diff --git a/mm/memory.c b/mm/memory.c +index f69fbc2511984..1a427097b71f0 100644 +--- a/mm/memory.c ++++ b/mm/memory.c +@@ -5262,6 +5262,127 @@ out: + } + EXPORT_SYMBOL_GPL(handle_mm_fault); + ++#ifdef CONFIG_LOCK_MM_AND_FIND_VMA ++#include ++ ++static inline bool get_mmap_lock_carefully(struct mm_struct *mm, struct pt_regs *regs) ++{ ++ /* Even if this succeeds, make it clear we *might* have slept */ ++ if (likely(mmap_read_trylock(mm))) { ++ might_sleep(); ++ return true; ++ } ++ ++ if (regs && !user_mode(regs)) { ++ unsigned long ip = instruction_pointer(regs); ++ if (!search_exception_tables(ip)) ++ return false; ++ } ++ ++ mmap_read_lock(mm); ++ return true; ++} ++ ++static inline bool mmap_upgrade_trylock(struct mm_struct *mm) ++{ ++ /* ++ * We don't have this operation yet. ++ * ++ * It should be easy enough to do: it's basically a ++ * atomic_long_try_cmpxchg_acquire() ++ * from RWSEM_READER_BIAS -> RWSEM_WRITER_LOCKED, but ++ * it also needs the proper lockdep magic etc. ++ */ ++ return false; ++} ++ ++static inline bool upgrade_mmap_lock_carefully(struct mm_struct *mm, struct pt_regs *regs) ++{ ++ mmap_read_unlock(mm); ++ if (regs && !user_mode(regs)) { ++ unsigned long ip = instruction_pointer(regs); ++ if (!search_exception_tables(ip)) ++ return false; ++ } ++ mmap_write_lock(mm); ++ return true; ++} ++ ++/* ++ * Helper for page fault handling. ++ * ++ * This is kind of equivalend to "mmap_read_lock()" followed ++ * by "find_extend_vma()", except it's a lot more careful about ++ * the locking (and will drop the lock on failure). ++ * ++ * For example, if we have a kernel bug that causes a page ++ * fault, we don't want to just use mmap_read_lock() to get ++ * the mm lock, because that would deadlock if the bug were ++ * to happen while we're holding the mm lock for writing. ++ * ++ * So this checks the exception tables on kernel faults in ++ * order to only do this all for instructions that are actually ++ * expected to fault. ++ * ++ * We can also actually take the mm lock for writing if we ++ * need to extend the vma, which helps the VM layer a lot. ++ */ ++struct vm_area_struct *lock_mm_and_find_vma(struct mm_struct *mm, ++ unsigned long addr, struct pt_regs *regs) ++{ ++ struct vm_area_struct *vma; ++ ++ if (!get_mmap_lock_carefully(mm, regs)) ++ return NULL; ++ ++ vma = find_vma(mm, addr); ++ if (likely(vma && (vma->vm_start <= addr))) ++ return vma; ++ ++ /* ++ * Well, dang. We might still be successful, but only ++ * if we can extend a vma to do so. ++ */ ++ if (!vma || !(vma->vm_flags & VM_GROWSDOWN)) { ++ mmap_read_unlock(mm); ++ return NULL; ++ } ++ ++ /* ++ * We can try to upgrade the mmap lock atomically, ++ * in which case we can continue to use the vma ++ * we already looked up. ++ * ++ * Otherwise we'll have to drop the mmap lock and ++ * re-take it, and also look up the vma again, ++ * re-checking it. ++ */ ++ if (!mmap_upgrade_trylock(mm)) { ++ if (!upgrade_mmap_lock_carefully(mm, regs)) ++ return NULL; ++ ++ vma = find_vma(mm, addr); ++ if (!vma) ++ goto fail; ++ if (vma->vm_start <= addr) ++ goto success; ++ if (!(vma->vm_flags & VM_GROWSDOWN)) ++ goto fail; ++ } ++ ++ if (expand_stack(vma, addr)) ++ goto fail; ++ ++success: ++ mmap_write_downgrade(mm); ++ return vma; ++ ++fail: ++ mmap_write_unlock(mm); ++ return NULL; ++} ++#endif ++ + #ifdef CONFIG_PER_VMA_LOCK + /* + * Lookup and lock a VMA under RCU protection. Returned VMA is guaranteed to be +-- +cgit diff --git a/tests/cases/CVE-2023-3389.patch b/tests/cases/CVE-2023-3389.patch new file mode 100644 index 0000000..4156cc3 --- /dev/null +++ b/tests/cases/CVE-2023-3389.patch @@ -0,0 +1,44 @@ +From 4716c73b188566865bdd79c3a6709696a224ac04 Mon Sep 17 00:00:00 2001 +From: Jens Axboe +Date: Fri, 16 Jun 2023 21:12:06 -0600 +Subject: io_uring: hold uring mutex around poll removal + +Snipped from commit 9ca9fb24d5febccea354089c41f96a8ad0d853f8 upstream. + +While reworking the poll hashing in the v6.0 kernel, we ended up +grabbing the ctx->uring_lock in poll update/removal. This also fixed +a bug with linked timeouts racing with timeout expiry and poll +removal. + +Bring back just the locking fix for that. + +Reported-and-tested-by: Querijn Voet +Signed-off-by: Jens Axboe +Signed-off-by: Greg Kroah-Hartman +--- + io_uring/io_uring.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c +index fd799567fc23a..58e0631143984 100644 +--- a/io_uring/io_uring.c ++++ b/io_uring/io_uring.c +@@ -5966,6 +5966,8 @@ static int io_poll_update(struct io_kiocb *req, unsigned int issue_flags) + struct io_kiocb *preq; + int ret2, ret = 0; + ++ io_ring_submit_lock(ctx, !(issue_flags & IO_URING_F_NONBLOCK)); ++ + spin_lock(&ctx->completion_lock); + preq = io_poll_find(ctx, req->poll_update.old_user_data, true); + if (!preq || !io_poll_disarm(preq)) { +@@ -5997,6 +5999,7 @@ out: + req_set_fail(req); + /* complete update request, we're done with it */ + io_req_complete(req, ret); ++ io_ring_submit_unlock(ctx, !(issue_flags & IO_URING_F_NONBLOCK)); + return 0; + } + +-- +cgit diff --git a/tests/cases/CVE-2023-3390.patch b/tests/cases/CVE-2023-3390.patch new file mode 100644 index 0000000..b84ba24 --- /dev/null +++ b/tests/cases/CVE-2023-3390.patch @@ -0,0 +1,70 @@ +From 1240eb93f0616b21c675416516ff3d74798fdc97 Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Thu, 8 Jun 2023 02:32:02 +0200 +Subject: netfilter: nf_tables: incorrect error path handling with + NFT_MSG_NEWRULE + +In case of error when adding a new rule that refers to an anonymous set, +deactivate expressions via NFT_TRANS_PREPARE state, not NFT_TRANS_RELEASE. +Thus, the lookup expression marks anonymous sets as inactive in the next +generation to ensure it is not reachable in this transaction anymore and +decrement the set refcount as introduced by c1592a89942e ("netfilter: +nf_tables: deactivate anonymous set from preparation phase"). The abort +step takes care of undoing the anonymous set. + +This is also consistent with rule deletion, where NFT_TRANS_PREPARE is +used. Note that this error path is exercised in the preparation step of +the commit protocol. This patch replaces nf_tables_rule_release() by the +deactivate and destroy calls, this time with NFT_TRANS_PREPARE. + +Due to this incorrect error handling, it is possible to access a +dangling pointer to the anonymous set that remains in the transaction +list. + +[1009.379054] BUG: KASAN: use-after-free in nft_set_lookup_global+0x147/0x1a0 [nf_tables] +[1009.379106] Read of size 8 at addr ffff88816c4c8020 by task nft-rule-add/137110 +[1009.379116] CPU: 7 PID: 137110 Comm: nft-rule-add Not tainted 6.4.0-rc4+ #256 +[1009.379128] Call Trace: +[1009.379132] +[1009.379135] dump_stack_lvl+0x33/0x50 +[1009.379146] ? nft_set_lookup_global+0x147/0x1a0 [nf_tables] +[1009.379191] print_address_description.constprop.0+0x27/0x300 +[1009.379201] kasan_report+0x107/0x120 +[1009.379210] ? nft_set_lookup_global+0x147/0x1a0 [nf_tables] +[1009.379255] nft_set_lookup_global+0x147/0x1a0 [nf_tables] +[1009.379302] nft_lookup_init+0xa5/0x270 [nf_tables] +[1009.379350] nf_tables_newrule+0x698/0xe50 [nf_tables] +[1009.379397] ? nf_tables_rule_release+0xe0/0xe0 [nf_tables] +[1009.379441] ? kasan_unpoison+0x23/0x50 +[1009.379450] nfnetlink_rcv_batch+0x97c/0xd90 [nfnetlink] +[1009.379470] ? nfnetlink_rcv_msg+0x480/0x480 [nfnetlink] +[1009.379485] ? __alloc_skb+0xb8/0x1e0 +[1009.379493] ? __alloc_skb+0xb8/0x1e0 +[1009.379502] ? entry_SYSCALL_64_after_hwframe+0x46/0xb0 +[1009.379509] ? unwind_get_return_address+0x2a/0x40 +[1009.379517] ? write_profile+0xc0/0xc0 +[1009.379524] ? avc_lookup+0x8f/0xc0 +[1009.379532] ? __rcu_read_unlock+0x43/0x60 + +Fixes: 958bee14d071 ("netfilter: nf_tables: use new transaction infrastructure to handle sets") +Signed-off-by: Pablo Neira Ayuso +--- + net/netfilter/nf_tables_api.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c +index 3bb0800b3849a7..69bceefaa5c80e 100644 +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -3844,7 +3844,8 @@ err_destroy_flow_rule: + if (flow) + nft_flow_rule_destroy(flow); + err_release_rule: +- nf_tables_rule_release(&ctx, rule); ++ nft_rule_expr_deactivate(&ctx, rule, NFT_TRANS_PREPARE); ++ nf_tables_rule_destroy(&ctx, rule); + err_release_expr: + for (i = 0; i < n; i++) { + if (expr_info[i].ops) { +-- +cgit 1.2.3-korg diff --git a/tests/cases/CVE-2023-35001.patch b/tests/cases/CVE-2023-35001.patch new file mode 100644 index 0000000..459f1fd --- /dev/null +++ b/tests/cases/CVE-2023-35001.patch @@ -0,0 +1,210 @@ +From caf3ef7468f7534771b5c44cd8dbd6f7f87c2cbd Mon Sep 17 00:00:00 2001 +From: Thadeu Lima de Souza Cascardo +Date: Wed, 5 Jul 2023 18:05:35 -0300 +Subject: netfilter: nf_tables: prevent OOB access in nft_byteorder_eval + +When evaluating byteorder expressions with size 2, a union with 32-bit and +16-bit members is used. Since the 16-bit members are aligned to 32-bit, +the array accesses will be out-of-bounds. + +It may lead to a stack-out-of-bounds access like the one below: + +[ 23.095215] ================================================================== +[ 23.095625] BUG: KASAN: stack-out-of-bounds in nft_byteorder_eval+0x13c/0x320 +[ 23.096020] Read of size 2 at addr ffffc90000007948 by task ping/115 +[ 23.096358] +[ 23.096456] CPU: 0 PID: 115 Comm: ping Not tainted 6.4.0+ #413 +[ 23.096770] Call Trace: +[ 23.096910] +[ 23.097030] dump_stack_lvl+0x60/0xc0 +[ 23.097218] print_report+0xcf/0x630 +[ 23.097388] ? nft_byteorder_eval+0x13c/0x320 +[ 23.097577] ? kasan_addr_to_slab+0xd/0xc0 +[ 23.097760] ? nft_byteorder_eval+0x13c/0x320 +[ 23.097949] kasan_report+0xc9/0x110 +[ 23.098106] ? nft_byteorder_eval+0x13c/0x320 +[ 23.098298] __asan_load2+0x83/0xd0 +[ 23.098453] nft_byteorder_eval+0x13c/0x320 +[ 23.098659] nft_do_chain+0x1c8/0xc50 +[ 23.098852] ? __pfx_nft_do_chain+0x10/0x10 +[ 23.099078] ? __kasan_check_read+0x11/0x20 +[ 23.099295] ? __pfx___lock_acquire+0x10/0x10 +[ 23.099535] ? __pfx___lock_acquire+0x10/0x10 +[ 23.099745] ? __kasan_check_read+0x11/0x20 +[ 23.099929] nft_do_chain_ipv4+0xfe/0x140 +[ 23.100105] ? __pfx_nft_do_chain_ipv4+0x10/0x10 +[ 23.100327] ? lock_release+0x204/0x400 +[ 23.100515] ? nf_hook.constprop.0+0x340/0x550 +[ 23.100779] nf_hook_slow+0x6c/0x100 +[ 23.100977] ? __pfx_nft_do_chain_ipv4+0x10/0x10 +[ 23.101223] nf_hook.constprop.0+0x334/0x550 +[ 23.101443] ? __pfx_ip_local_deliver_finish+0x10/0x10 +[ 23.101677] ? __pfx_nf_hook.constprop.0+0x10/0x10 +[ 23.101882] ? __pfx_ip_rcv_finish+0x10/0x10 +[ 23.102071] ? __pfx_ip_local_deliver_finish+0x10/0x10 +[ 23.102291] ? rcu_read_lock_held+0x4b/0x70 +[ 23.102481] ip_local_deliver+0xbb/0x110 +[ 23.102665] ? __pfx_ip_rcv+0x10/0x10 +[ 23.102839] ip_rcv+0x199/0x2a0 +[ 23.102980] ? __pfx_ip_rcv+0x10/0x10 +[ 23.103140] __netif_receive_skb_one_core+0x13e/0x150 +[ 23.103362] ? __pfx___netif_receive_skb_one_core+0x10/0x10 +[ 23.103647] ? mark_held_locks+0x48/0xa0 +[ 23.103819] ? process_backlog+0x36c/0x380 +[ 23.103999] __netif_receive_skb+0x23/0xc0 +[ 23.104179] process_backlog+0x91/0x380 +[ 23.104350] __napi_poll.constprop.0+0x66/0x360 +[ 23.104589] ? net_rx_action+0x1cb/0x610 +[ 23.104811] net_rx_action+0x33e/0x610 +[ 23.105024] ? _raw_spin_unlock+0x23/0x50 +[ 23.105257] ? __pfx_net_rx_action+0x10/0x10 +[ 23.105485] ? mark_held_locks+0x48/0xa0 +[ 23.105741] __do_softirq+0xfa/0x5ab +[ 23.105956] ? __dev_queue_xmit+0x765/0x1c00 +[ 23.106193] do_softirq.part.0+0x49/0xc0 +[ 23.106423] +[ 23.106547] +[ 23.106670] __local_bh_enable_ip+0xf5/0x120 +[ 23.106903] __dev_queue_xmit+0x789/0x1c00 +[ 23.107131] ? __pfx___dev_queue_xmit+0x10/0x10 +[ 23.107381] ? find_held_lock+0x8e/0xb0 +[ 23.107585] ? lock_release+0x204/0x400 +[ 23.107798] ? neigh_resolve_output+0x185/0x350 +[ 23.108049] ? mark_held_locks+0x48/0xa0 +[ 23.108265] ? neigh_resolve_output+0x185/0x350 +[ 23.108514] neigh_resolve_output+0x246/0x350 +[ 23.108753] ? neigh_resolve_output+0x246/0x350 +[ 23.109003] ip_finish_output2+0x3c3/0x10b0 +[ 23.109250] ? __pfx_ip_finish_output2+0x10/0x10 +[ 23.109510] ? __pfx_nf_hook+0x10/0x10 +[ 23.109732] __ip_finish_output+0x217/0x390 +[ 23.109978] ip_finish_output+0x2f/0x130 +[ 23.110207] ip_output+0xc9/0x170 +[ 23.110404] ip_push_pending_frames+0x1a0/0x240 +[ 23.110652] raw_sendmsg+0x102e/0x19e0 +[ 23.110871] ? __pfx_raw_sendmsg+0x10/0x10 +[ 23.111093] ? lock_release+0x204/0x400 +[ 23.111304] ? __mod_lruvec_page_state+0x148/0x330 +[ 23.111567] ? find_held_lock+0x8e/0xb0 +[ 23.111777] ? find_held_lock+0x8e/0xb0 +[ 23.111993] ? __rcu_read_unlock+0x7c/0x2f0 +[ 23.112225] ? aa_sk_perm+0x18a/0x550 +[ 23.112431] ? filemap_map_pages+0x4f1/0x900 +[ 23.112665] ? __pfx_aa_sk_perm+0x10/0x10 +[ 23.112880] ? find_held_lock+0x8e/0xb0 +[ 23.113098] inet_sendmsg+0xa0/0xb0 +[ 23.113297] ? inet_sendmsg+0xa0/0xb0 +[ 23.113500] ? __pfx_inet_sendmsg+0x10/0x10 +[ 23.113727] sock_sendmsg+0xf4/0x100 +[ 23.113924] ? move_addr_to_kernel.part.0+0x4f/0xa0 +[ 23.114190] __sys_sendto+0x1d4/0x290 +[ 23.114391] ? __pfx___sys_sendto+0x10/0x10 +[ 23.114621] ? __pfx_mark_lock.part.0+0x10/0x10 +[ 23.114869] ? lock_release+0x204/0x400 +[ 23.115076] ? find_held_lock+0x8e/0xb0 +[ 23.115287] ? rcu_is_watching+0x23/0x60 +[ 23.115503] ? __rseq_handle_notify_resume+0x6e2/0x860 +[ 23.115778] ? __kasan_check_write+0x14/0x30 +[ 23.116008] ? blkcg_maybe_throttle_current+0x8d/0x770 +[ 23.116285] ? mark_held_locks+0x28/0xa0 +[ 23.116503] ? do_syscall_64+0x37/0x90 +[ 23.116713] __x64_sys_sendto+0x7f/0xb0 +[ 23.116924] do_syscall_64+0x59/0x90 +[ 23.117123] ? irqentry_exit_to_user_mode+0x25/0x30 +[ 23.117387] ? irqentry_exit+0x77/0xb0 +[ 23.117593] ? exc_page_fault+0x92/0x140 +[ 23.117806] entry_SYSCALL_64_after_hwframe+0x6e/0xd8 +[ 23.118081] RIP: 0033:0x7f744aee2bba +[ 23.118282] Code: d8 64 89 02 48 c7 c0 ff ff ff ff eb b8 0f 1f 00 f3 0f 1e fa 41 89 ca 64 8b 04 25 18 00 00 00 85 c0 75 15 b8 2c 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 7e c3 0f 1f 44 00 00 41 54 48 83 ec 30 44 89 +[ 23.119237] RSP: 002b:00007ffd04a7c9f8 EFLAGS: 00000246 ORIG_RAX: 000000000000002c +[ 23.119644] RAX: ffffffffffffffda RBX: 00007ffd04a7e0a0 RCX: 00007f744aee2bba +[ 23.120023] RDX: 0000000000000040 RSI: 000056488e9e6300 RDI: 0000000000000003 +[ 23.120413] RBP: 000056488e9e6300 R08: 00007ffd04a80320 R09: 0000000000000010 +[ 23.120809] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000040 +[ 23.121219] R13: 00007ffd04a7dc38 R14: 00007ffd04a7ca00 R15: 00007ffd04a7e0a0 +[ 23.121617] +[ 23.121749] +[ 23.121845] The buggy address belongs to the virtual mapping at +[ 23.121845] [ffffc90000000000, ffffc90000009000) created by: +[ 23.121845] irq_init_percpu_irqstack+0x1cf/0x270 +[ 23.122707] +[ 23.122803] The buggy address belongs to the physical page: +[ 23.123104] page:0000000072ac19f0 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x24a09 +[ 23.123609] flags: 0xfffffc0001000(reserved|node=0|zone=1|lastcpupid=0x1fffff) +[ 23.123998] page_type: 0xffffffff() +[ 23.124194] raw: 000fffffc0001000 ffffea0000928248 ffffea0000928248 0000000000000000 +[ 23.124610] raw: 0000000000000000 0000000000000000 00000001ffffffff 0000000000000000 +[ 23.125023] page dumped because: kasan: bad access detected +[ 23.125326] +[ 23.125421] Memory state around the buggy address: +[ 23.125682] ffffc90000007800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +[ 23.126072] ffffc90000007880: 00 00 00 00 00 f1 f1 f1 f1 f1 f1 00 00 f2 f2 00 +[ 23.126455] >ffffc90000007900: 00 00 00 00 00 00 00 00 00 f2 f2 f2 f2 00 00 00 +[ 23.126840] ^ +[ 23.127138] ffffc90000007980: 00 00 00 00 00 00 00 00 00 00 00 00 00 f3 f3 f3 +[ 23.127522] ffffc90000007a00: f3 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 +[ 23.127906] ================================================================== +[ 23.128324] Disabling lock debugging due to kernel taint + +Using simple s16 pointers for the 16-bit accesses fixes the problem. For +the 32-bit accesses, src and dst can be used directly. + +Fixes: 96518518cc41 ("netfilter: add nftables") +Cc: stable@vger.kernel.org +Reported-by: Tanguy DUBROCA (@SidewayRE) from @Synacktiv working with ZDI +Signed-off-by: Thadeu Lima de Souza Cascardo +Reviewed-by: Florian Westphal +Signed-off-by: Pablo Neira Ayuso +--- + net/netfilter/nft_byteorder.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/net/netfilter/nft_byteorder.c b/net/netfilter/nft_byteorder.c +index 9a85e797ed58b..e596d1a842f70 100644 +--- a/net/netfilter/nft_byteorder.c ++++ b/net/netfilter/nft_byteorder.c +@@ -30,11 +30,11 @@ void nft_byteorder_eval(const struct nft_expr *expr, + const struct nft_byteorder *priv = nft_expr_priv(expr); + u32 *src = ®s->data[priv->sreg]; + u32 *dst = ®s->data[priv->dreg]; +- union { u32 u32; u16 u16; } *s, *d; ++ u16 *s16, *d16; + unsigned int i; + +- s = (void *)src; +- d = (void *)dst; ++ s16 = (void *)src; ++ d16 = (void *)dst; + + switch (priv->size) { + case 8: { +@@ -62,11 +62,11 @@ void nft_byteorder_eval(const struct nft_expr *expr, + switch (priv->op) { + case NFT_BYTEORDER_NTOH: + for (i = 0; i < priv->len / 4; i++) +- d[i].u32 = ntohl((__force __be32)s[i].u32); ++ dst[i] = ntohl((__force __be32)src[i]); + break; + case NFT_BYTEORDER_HTON: + for (i = 0; i < priv->len / 4; i++) +- d[i].u32 = (__force __u32)htonl(s[i].u32); ++ dst[i] = (__force __u32)htonl(src[i]); + break; + } + break; +@@ -74,11 +74,11 @@ void nft_byteorder_eval(const struct nft_expr *expr, + switch (priv->op) { + case NFT_BYTEORDER_NTOH: + for (i = 0; i < priv->len / 2; i++) +- d[i].u16 = ntohs((__force __be16)s[i].u16); ++ d16[i] = ntohs((__force __be16)s16[i]); + break; + case NFT_BYTEORDER_HTON: + for (i = 0; i < priv->len / 2; i++) +- d[i].u16 = (__force __u16)htons(s[i].u16); ++ d16[i] = (__force __u16)htons(s16[i]); + break; + } + break; +-- +cgit diff --git a/tests/cases/CVE-2023-3609.patch b/tests/cases/CVE-2023-3609.patch new file mode 100644 index 0000000..e4b3eb2 --- /dev/null +++ b/tests/cases/CVE-2023-3609.patch @@ -0,0 +1,72 @@ +From 04c55383fa5689357bcdd2c8036725a55ed632bc Mon Sep 17 00:00:00 2001 +From: Lee Jones +Date: Thu, 8 Jun 2023 08:29:03 +0100 +Subject: net/sched: cls_u32: Fix reference counter leak leading to overflow + +In the event of a failure in tcf_change_indev(), u32_set_parms() will +immediately return without decrementing the recently incremented +reference counter. If this happens enough times, the counter will +rollover and the reference freed, leading to a double free which can be +used to do 'bad things'. + +In order to prevent this, move the point of possible failure above the +point where the reference counter is incremented. Also save any +meaningful return values to be applied to the return data at the +appropriate point in time. + +This issue was caught with KASAN. + +Fixes: 705c7091262d ("net: sched: cls_u32: no need to call tcf_exts_change for newly allocated struct") +Suggested-by: Eric Dumazet +Signed-off-by: Lee Jones +Reviewed-by: Eric Dumazet +Acked-by: Jamal Hadi Salim +Signed-off-by: David S. Miller +--- + net/sched/cls_u32.c | 18 ++++++++++-------- + 1 file changed, 10 insertions(+), 8 deletions(-) + +diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c +index 4e2e269f121f8a..d15d50de798028 100644 +--- a/net/sched/cls_u32.c ++++ b/net/sched/cls_u32.c +@@ -718,13 +718,19 @@ static int u32_set_parms(struct net *net, struct tcf_proto *tp, + struct nlattr *est, u32 flags, u32 fl_flags, + struct netlink_ext_ack *extack) + { +- int err; ++ int err, ifindex = -1; + + err = tcf_exts_validate_ex(net, tp, tb, est, &n->exts, flags, + fl_flags, extack); + if (err < 0) + return err; + ++ if (tb[TCA_U32_INDEV]) { ++ ifindex = tcf_change_indev(net, tb[TCA_U32_INDEV], extack); ++ if (ifindex < 0) ++ return -EINVAL; ++ } ++ + if (tb[TCA_U32_LINK]) { + u32 handle = nla_get_u32(tb[TCA_U32_LINK]); + struct tc_u_hnode *ht_down = NULL, *ht_old; +@@ -759,13 +765,9 @@ static int u32_set_parms(struct net *net, struct tcf_proto *tp, + tcf_bind_filter(tp, &n->res, base); + } + +- if (tb[TCA_U32_INDEV]) { +- int ret; +- ret = tcf_change_indev(net, tb[TCA_U32_INDEV], extack); +- if (ret < 0) +- return -EINVAL; +- n->ifindex = ret; +- } ++ if (ifindex >= 0) ++ n->ifindex = ifindex; ++ + return 0; + } + +-- +cgit 1.2.3-korg diff --git a/tests/cases/CVE-2023-3611.patch b/tests/cases/CVE-2023-3611.patch new file mode 100644 index 0000000..7addb3f --- /dev/null +++ b/tests/cases/CVE-2023-3611.patch @@ -0,0 +1,90 @@ +From 3e337087c3b5805fe0b8a46ba622a962880b5d64 Mon Sep 17 00:00:00 2001 +From: Pedro Tammela +Date: Tue, 11 Jul 2023 18:01:02 -0300 +Subject: net/sched: sch_qfq: account for stab overhead in qfq_enqueue + +Lion says: +------- +In the QFQ scheduler a similar issue to CVE-2023-31436 +persists. + +Consider the following code in net/sched/sch_qfq.c: + +static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch, + struct sk_buff **to_free) +{ + unsigned int len = qdisc_pkt_len(skb), gso_segs; + + // ... + + if (unlikely(cl->agg->lmax < len)) { + pr_debug("qfq: increasing maxpkt from %u to %u for class %u", + cl->agg->lmax, len, cl->common.classid); + err = qfq_change_agg(sch, cl, cl->agg->class_weight, len); + if (err) { + cl->qstats.drops++; + return qdisc_drop(skb, sch, to_free); + } + + // ... + + } + +Similarly to CVE-2023-31436, "lmax" is increased without any bounds +checks according to the packet length "len". Usually this would not +impose a problem because packet sizes are naturally limited. + +This is however not the actual packet length, rather the +"qdisc_pkt_len(skb)" which might apply size transformations according to +"struct qdisc_size_table" as created by "qdisc_get_stab()" in +net/sched/sch_api.c if the TCA_STAB option was set when modifying the qdisc. + +A user may choose virtually any size using such a table. + +As a result the same issue as in CVE-2023-31436 can occur, allowing heap +out-of-bounds read / writes in the kmalloc-8192 cache. +------- + +We can create the issue with the following commands: + +tc qdisc add dev $DEV root handle 1: stab mtu 2048 tsize 512 mpu 0 \ +overhead 999999999 linklayer ethernet qfq +tc class add dev $DEV parent 1: classid 1:1 htb rate 6mbit burst 15k +tc filter add dev $DEV parent 1: matchall classid 1:1 +ping -I $DEV 1.1.1.2 + +This is caused by incorrectly assuming that qdisc_pkt_len() returns a +length within the QFQ_MIN_LMAX < len < QFQ_MAX_LMAX. + +Fixes: 462dbc9101ac ("pkt_sched: QFQ Plus: fair-queueing service at DRR cost") +Reported-by: Lion +Reviewed-by: Eric Dumazet +Signed-off-by: Jamal Hadi Salim +Signed-off-by: Pedro Tammela +Reviewed-by: Simon Horman +Signed-off-by: Paolo Abeni +--- + net/sched/sch_qfq.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c +index 63a5b277c117f3..befaf74b33caa2 100644 +--- a/net/sched/sch_qfq.c ++++ b/net/sched/sch_qfq.c +@@ -381,8 +381,13 @@ static int qfq_change_agg(struct Qdisc *sch, struct qfq_class *cl, u32 weight, + u32 lmax) + { + struct qfq_sched *q = qdisc_priv(sch); +- struct qfq_aggregate *new_agg = qfq_find_agg(q, lmax, weight); ++ struct qfq_aggregate *new_agg; + ++ /* 'lmax' can range from [QFQ_MIN_LMAX, pktlen + stab overhead] */ ++ if (lmax > QFQ_MAX_LMAX) ++ return -EINVAL; ++ ++ new_agg = qfq_find_agg(q, lmax, weight); + if (new_agg == NULL) { /* create new aggregate */ + new_agg = kzalloc(sizeof(*new_agg), GFP_ATOMIC); + if (new_agg == NULL) +-- +cgit 1.2.3-korg diff --git a/tests/cases/CVE-2023-3776.patch b/tests/cases/CVE-2023-3776.patch new file mode 100644 index 0000000..11c2f7c --- /dev/null +++ b/tests/cases/CVE-2023-3776.patch @@ -0,0 +1,56 @@ +From 0323bce598eea038714f941ce2b22541c46d488f Mon Sep 17 00:00:00 2001 +From: M A Ramdhan +Date: Wed, 5 Jul 2023 12:15:30 -0400 +Subject: net/sched: cls_fw: Fix improper refcount update leads to + use-after-free + +In the event of a failure in tcf_change_indev(), fw_set_parms() will +immediately return an error after incrementing or decrementing +reference counter in tcf_bind_filter(). If attacker can control +reference counter to zero and make reference freed, leading to +use after free. + +In order to prevent this, move the point of possible failure above the +point where the TC_FW_CLASSID is handled. + +Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") +Reported-by: M A Ramdhan +Signed-off-by: M A Ramdhan +Acked-by: Jamal Hadi Salim +Reviewed-by: Pedro Tammela +Message-ID: <20230705161530.52003-1-ramdhan@starlabs.sg> +Signed-off-by: Jakub Kicinski +--- + net/sched/cls_fw.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c +index ae9439a6c56c90..8641f805931793 100644 +--- a/net/sched/cls_fw.c ++++ b/net/sched/cls_fw.c +@@ -212,11 +212,6 @@ static int fw_set_parms(struct net *net, struct tcf_proto *tp, + if (err < 0) + return err; + +- if (tb[TCA_FW_CLASSID]) { +- f->res.classid = nla_get_u32(tb[TCA_FW_CLASSID]); +- tcf_bind_filter(tp, &f->res, base); +- } +- + if (tb[TCA_FW_INDEV]) { + int ret; + ret = tcf_change_indev(net, tb[TCA_FW_INDEV], extack); +@@ -233,6 +228,11 @@ static int fw_set_parms(struct net *net, struct tcf_proto *tp, + } else if (head->mask != 0xFFFFFFFF) + return err; + ++ if (tb[TCA_FW_CLASSID]) { ++ f->res.classid = nla_get_u32(tb[TCA_FW_CLASSID]); ++ tcf_bind_filter(tp, &f->res, base); ++ } ++ + return 0; + } + +-- +cgit 1.2.3-korg diff --git a/tests/cases/CVE-2023-3812.patch b/tests/cases/CVE-2023-3812.patch new file mode 100644 index 0000000..0c809fe --- /dev/null +++ b/tests/cases/CVE-2023-3812.patch @@ -0,0 +1,90 @@ +From 363a5328f4b0517e59572118ccfb7c626d81dca9 Mon Sep 17 00:00:00 2001 +From: Ziyang Xuan +Date: Sat, 29 Oct 2022 17:41:01 +0800 +Subject: net: tun: fix bugs for oversize packet when napi frags enabled + +Recently, we got two syzkaller problems because of oversize packet +when napi frags enabled. + +One of the problems is because the first seg size of the iov_iter +from user space is very big, it is 2147479538 which is bigger than +the threshold value for bail out early in __alloc_pages(). And +skb->pfmemalloc is true, __kmalloc_reserve() would use pfmemalloc +reserves without __GFP_NOWARN flag. Thus we got a warning as following: + +======================================================== +WARNING: CPU: 1 PID: 17965 at mm/page_alloc.c:5295 __alloc_pages+0x1308/0x16c4 mm/page_alloc.c:5295 +... +Call trace: + __alloc_pages+0x1308/0x16c4 mm/page_alloc.c:5295 + __alloc_pages_node include/linux/gfp.h:550 [inline] + alloc_pages_node include/linux/gfp.h:564 [inline] + kmalloc_large_node+0x94/0x350 mm/slub.c:4038 + __kmalloc_node_track_caller+0x620/0x8e4 mm/slub.c:4545 + __kmalloc_reserve.constprop.0+0x1e4/0x2b0 net/core/skbuff.c:151 + pskb_expand_head+0x130/0x8b0 net/core/skbuff.c:1654 + __skb_grow include/linux/skbuff.h:2779 [inline] + tun_napi_alloc_frags+0x144/0x610 drivers/net/tun.c:1477 + tun_get_user+0x31c/0x2010 drivers/net/tun.c:1835 + tun_chr_write_iter+0x98/0x100 drivers/net/tun.c:2036 + +The other problem is because odd IPv6 packets without NEXTHDR_NONE +extension header and have big packet length, it is 2127925 which is +bigger than ETH_MAX_MTU(65535). After ipv6_gso_pull_exthdrs() in +ipv6_gro_receive(), network_header offset and transport_header offset +are all bigger than U16_MAX. That would trigger skb->network_header +and skb->transport_header overflow error, because they are all '__u16' +type. Eventually, it would affect the value for __skb_push(skb, value), +and make it be a big value. After __skb_push() in ipv6_gro_receive(), +skb->data would less than skb->head, an out of bounds memory bug occurred. +That would trigger the problem as following: + +================================================================== +BUG: KASAN: use-after-free in eth_type_trans+0x100/0x260 +... +Call trace: + dump_backtrace+0xd8/0x130 + show_stack+0x1c/0x50 + dump_stack_lvl+0x64/0x7c + print_address_description.constprop.0+0xbc/0x2e8 + print_report+0x100/0x1e4 + kasan_report+0x80/0x120 + __asan_load8+0x78/0xa0 + eth_type_trans+0x100/0x260 + napi_gro_frags+0x164/0x550 + tun_get_user+0xda4/0x1270 + tun_chr_write_iter+0x74/0x130 + do_iter_readv_writev+0x130/0x1ec + do_iter_write+0xbc/0x1e0 + vfs_writev+0x13c/0x26c + +To fix the problems, restrict the packet size less than +(ETH_MAX_MTU - NET_SKB_PAD - NET_IP_ALIGN) which has considered reserved +skb space in napi_alloc_skb() because transport_header is an offset from +skb->head. Add len check in tun_napi_alloc_frags() simply. + +Fixes: 90e33d459407 ("tun: enable napi_gro_frags() for TUN/TAP driver") +Signed-off-by: Ziyang Xuan +Reviewed-by: Eric Dumazet +Link: https://lore.kernel.org/r/20221029094101.1653855-1-william.xuanziyang@huawei.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/tun.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/tun.c b/drivers/net/tun.c +index 27c6d235cbda3..946628050f282 100644 +--- a/drivers/net/tun.c ++++ b/drivers/net/tun.c +@@ -1459,7 +1459,8 @@ static struct sk_buff *tun_napi_alloc_frags(struct tun_file *tfile, + int err; + int i; + +- if (it->nr_segs > MAX_SKB_FRAGS + 1) ++ if (it->nr_segs > MAX_SKB_FRAGS + 1 || ++ len > (ETH_MAX_MTU - NET_SKB_PAD - NET_IP_ALIGN)) + return ERR_PTR(-EMSGSIZE); + + local_bh_disable(); +-- +cgit diff --git a/tests/cases/CVE-2023-4004.patch b/tests/cases/CVE-2023-4004.patch new file mode 100644 index 0000000..badf60e --- /dev/null +++ b/tests/cases/CVE-2023-4004.patch @@ -0,0 +1,57 @@ +From 87b5a5c209405cb6b57424cdfa226a6dbd349232 Mon Sep 17 00:00:00 2001 +From: Florian Westphal +Date: Wed, 19 Jul 2023 21:08:21 +0200 +Subject: netfilter: nft_set_pipapo: fix improper element removal + +end key should be equal to start unless NFT_SET_EXT_KEY_END is present. + +Its possible to add elements that only have a start key +("{ 1.0.0.0 . 2.0.0.0 }") without an internval end. + +Insertion treats this via: + +if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END)) + end = (const u8 *)nft_set_ext_key_end(ext)->data; +else + end = start; + +but removal side always uses nft_set_ext_key_end(). +This is wrong and leads to garbage remaining in the set after removal +next lookup/insert attempt will give: + +BUG: KASAN: slab-use-after-free in pipapo_get+0x8eb/0xb90 +Read of size 1 at addr ffff888100d50586 by task nft-pipapo_uaf_/1399 +Call Trace: + kasan_report+0x105/0x140 + pipapo_get+0x8eb/0xb90 + nft_pipapo_insert+0x1dc/0x1710 + nf_tables_newsetelem+0x31f5/0x4e00 + .. + +Fixes: 3c4287f62044 ("nf_tables: Add set type for arbitrary concatenation of ranges") +Reported-by: lonial con +Reviewed-by: Stefano Brivio +Signed-off-by: Florian Westphal +--- + net/netfilter/nft_set_pipapo.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c +index db526cb7a4858e..49915a2a58eb77 100644 +--- a/net/netfilter/nft_set_pipapo.c ++++ b/net/netfilter/nft_set_pipapo.c +@@ -1929,7 +1929,11 @@ static void nft_pipapo_remove(const struct net *net, const struct nft_set *set, + int i, start, rules_fx; + + match_start = data; +- match_end = (const u8 *)nft_set_ext_key_end(&e->ext)->data; ++ ++ if (nft_set_ext_exists(&e->ext, NFT_SET_EXT_KEY_END)) ++ match_end = (const u8 *)nft_set_ext_key_end(&e->ext)->data; ++ else ++ match_end = data; + + start = first_rule; + rules_fx = rules_f0; +-- +cgit 1.2.3-korg diff --git a/tests/cases/CVE-2023-42753.patch b/tests/cases/CVE-2023-42753.patch new file mode 100644 index 0000000..3481721 --- /dev/null +++ b/tests/cases/CVE-2023-42753.patch @@ -0,0 +1,36 @@ +From 050d91c03b28ca479df13dfb02bcd2c60dd6a878 Mon Sep 17 00:00:00 2001 +From: Kyle Zeng +Date: Tue, 5 Sep 2023 15:04:09 -0700 +Subject: netfilter: ipset: add the missing IP_SET_HASH_WITH_NET0 macro for + ip_set_hash_netportnet.c + +The missing IP_SET_HASH_WITH_NET0 macro in ip_set_hash_netportnet can +lead to the use of wrong `CIDR_POS(c)` for calculating array offsets, +which can lead to integer underflow. As a result, it leads to slab +out-of-bound access. +This patch adds back the IP_SET_HASH_WITH_NET0 macro to +ip_set_hash_netportnet to address the issue. + +Fixes: 886503f34d63 ("netfilter: ipset: actually allow allowable CIDR 0 in hash:net,port,net") +Suggested-by: Jozsef Kadlecsik +Signed-off-by: Kyle Zeng +Acked-by: Jozsef Kadlecsik +Signed-off-by: Florian Westphal +--- + net/netfilter/ipset/ip_set_hash_netportnet.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/net/netfilter/ipset/ip_set_hash_netportnet.c b/net/netfilter/ipset/ip_set_hash_netportnet.c +index 005a7ce87217e..bf4f91b78e1dc 100644 +--- a/net/netfilter/ipset/ip_set_hash_netportnet.c ++++ b/net/netfilter/ipset/ip_set_hash_netportnet.c +@@ -36,6 +36,7 @@ MODULE_ALIAS("ip_set_hash:net,port,net"); + #define IP_SET_HASH_WITH_PROTO + #define IP_SET_HASH_WITH_NETS + #define IPSET_NET_COUNT 2 ++#define IP_SET_HASH_WITH_NET0 + + /* IPv4 variant */ + +-- +cgit diff --git a/tests/cases/CVE-2023-4622.patch b/tests/cases/CVE-2023-4622.patch new file mode 100644 index 0000000..2a00e26 --- /dev/null +++ b/tests/cases/CVE-2023-4622.patch @@ -0,0 +1,136 @@ +From 790c2f9d15b594350ae9bca7b236f2b1859de02c Mon Sep 17 00:00:00 2001 +From: Kuniyuki Iwashima +Date: Mon, 21 Aug 2023 10:55:05 -0700 +Subject: af_unix: Fix null-ptr-deref in unix_stream_sendpage(). + +Bing-Jhong Billy Jheng reported null-ptr-deref in unix_stream_sendpage() +with detailed analysis and a nice repro. + +unix_stream_sendpage() tries to add data to the last skb in the peer's +recv queue without locking the queue. + +If the peer's FD is passed to another socket and the socket's FD is +passed to the peer, there is a loop between them. If we close both +sockets without receiving FD, the sockets will be cleaned up by garbage +collection. + +The garbage collection iterates such sockets and unlinks skb with +FD from the socket's receive queue under the queue's lock. + +So, there is a race where unix_stream_sendpage() could access an skb +locklessly that is being released by garbage collection, resulting in +use-after-free. + +To avoid the issue, unix_stream_sendpage() must lock the peer's recv +queue. + +Note the issue does not exist in 6.5+ thanks to the recent sendpage() +refactoring. + +This patch is originally written by Linus Torvalds. + +BUG: unable to handle page fault for address: ffff988004dd6870 +PF: supervisor read access in kernel mode +PF: error_code(0x0000) - not-present page +PGD 0 P4D 0 +PREEMPT SMP PTI +CPU: 4 PID: 297 Comm: garbage_uaf Not tainted 6.1.46 #1 +Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014 +RIP: 0010:kmem_cache_alloc_node+0xa2/0x1e0 +Code: c0 0f 84 32 01 00 00 41 83 fd ff 74 10 48 8b 00 48 c1 e8 3a 41 39 c5 0f 85 1c 01 00 00 41 8b 44 24 28 49 8b 3c 24 48 8d 4a 40 <49> 8b 1c 06 4c 89 f0 65 48 0f c7 0f 0f 94 c0 84 c0 74 a1 41 8b 44 +RSP: 0018:ffffc9000079fac0 EFLAGS: 00000246 +RAX: 0000000000000070 RBX: 0000000000000005 RCX: 000000000001a284 +RDX: 000000000001a244 RSI: 0000000000400cc0 RDI: 000000000002eee0 +RBP: 0000000000400cc0 R08: 0000000000400cc0 R09: 0000000000000003 +R10: 0000000000000001 R11: 0000000000000000 R12: ffff888003970f00 +R13: 00000000ffffffff R14: ffff988004dd6800 R15: 00000000000000e8 +FS: 00007f174d6f3600(0000) GS:ffff88807db00000(0000) knlGS:0000000000000000 +CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +CR2: ffff988004dd6870 CR3: 00000000092be000 CR4: 00000000007506e0 +PKRU: 55555554 +Call Trace: + + ? __die_body.cold+0x1a/0x1f + ? page_fault_oops+0xa9/0x1e0 + ? fixup_exception+0x1d/0x310 + ? exc_page_fault+0xa8/0x150 + ? asm_exc_page_fault+0x22/0x30 + ? kmem_cache_alloc_node+0xa2/0x1e0 + ? __alloc_skb+0x16c/0x1e0 + __alloc_skb+0x16c/0x1e0 + alloc_skb_with_frags+0x48/0x1e0 + sock_alloc_send_pskb+0x234/0x270 + unix_stream_sendmsg+0x1f5/0x690 + sock_sendmsg+0x5d/0x60 + ____sys_sendmsg+0x210/0x260 + ___sys_sendmsg+0x83/0xd0 + ? kmem_cache_alloc+0xc6/0x1c0 + ? avc_disable+0x20/0x20 + ? percpu_counter_add_batch+0x53/0xc0 + ? alloc_empty_file+0x5d/0xb0 + ? alloc_file+0x91/0x170 + ? alloc_file_pseudo+0x94/0x100 + ? __fget_light+0x9f/0x120 + __sys_sendmsg+0x54/0xa0 + do_syscall_64+0x3b/0x90 + entry_SYSCALL_64_after_hwframe+0x69/0xd3 +RIP: 0033:0x7f174d639a7d +Code: 28 89 54 24 1c 48 89 74 24 10 89 7c 24 08 e8 8a c1 f4 ff 8b 54 24 1c 48 8b 74 24 10 41 89 c0 8b 7c 24 08 b8 2e 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 33 44 89 c7 48 89 44 24 08 e8 de c1 f4 ff 48 +RSP: 002b:00007ffcb563ea50 EFLAGS: 00000293 ORIG_RAX: 000000000000002e +RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f174d639a7d +RDX: 0000000000000000 RSI: 00007ffcb563eab0 RDI: 0000000000000007 +RBP: 00007ffcb563eb10 R08: 0000000000000000 R09: 00000000ffffffff +R10: 00000000004040a0 R11: 0000000000000293 R12: 00007ffcb563ec28 +R13: 0000000000401398 R14: 0000000000403e00 R15: 00007f174d72c000 + + +Fixes: 869e7c62486e ("net: af_unix: implement stream sendpage support") +Reported-by: Bing-Jhong Billy Jheng +Reviewed-by: Bing-Jhong Billy Jheng +Co-developed-by: Linus Torvalds +Signed-off-by: Linus Torvalds +Signed-off-by: Kuniyuki Iwashima +Signed-off-by: Greg Kroah-Hartman +--- + net/unix/af_unix.c | 9 ++++----- + 1 file changed, 4 insertions(+), 5 deletions(-) + +diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c +index 78fa620a639818..ca31847a6c70cd 100644 +--- a/net/unix/af_unix.c ++++ b/net/unix/af_unix.c +@@ -2290,6 +2290,7 @@ static ssize_t unix_stream_sendpage(struct socket *socket, struct page *page, + + if (false) { + alloc_skb: ++ spin_unlock(&other->sk_receive_queue.lock); + unix_state_unlock(other); + mutex_unlock(&unix_sk(other)->iolock); + newskb = sock_alloc_send_pskb(sk, 0, 0, flags & MSG_DONTWAIT, +@@ -2329,6 +2330,7 @@ alloc_skb: + init_scm = false; + } + ++ spin_lock(&other->sk_receive_queue.lock); + skb = skb_peek_tail(&other->sk_receive_queue); + if (tail && tail == skb) { + skb = newskb; +@@ -2359,14 +2361,11 @@ alloc_skb: + refcount_add(size, &sk->sk_wmem_alloc); + + if (newskb) { +- err = unix_scm_to_skb(&scm, skb, false); +- if (err) +- goto err_state_unlock; +- spin_lock(&other->sk_receive_queue.lock); ++ unix_scm_to_skb(&scm, skb, false); + __skb_queue_tail(&other->sk_receive_queue, newskb); +- spin_unlock(&other->sk_receive_queue.lock); + } + ++ spin_unlock(&other->sk_receive_queue.lock); + unix_state_unlock(other); + mutex_unlock(&unix_sk(other)->iolock); + +-- +cgit 1.2.3-korg diff --git a/tests/cases/CVE-2023-5345.patch b/tests/cases/CVE-2023-5345.patch new file mode 100644 index 0000000..ff74c36 --- /dev/null +++ b/tests/cases/CVE-2023-5345.patch @@ -0,0 +1,30 @@ +From e6e43b8aa7cd3c3af686caf0c2e11819a886d705 Mon Sep 17 00:00:00 2001 +From: Quang Le +Date: Fri, 29 Sep 2023 00:44:13 +0700 +Subject: fs/smb/client: Reset password pointer to NULL + +Forget to reset ctx->password to NULL will lead to bug like double free + +Cc: stable@vger.kernel.org +Cc: Willy Tarreau +Reviewed-by: Namjae Jeon +Signed-off-by: Quang Le +Signed-off-by: Steve French +--- + fs/smb/client/fs_context.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c +index e45ce31bbda717..a3493da12ad1e6 100644 +--- a/fs/smb/client/fs_context.c ++++ b/fs/smb/client/fs_context.c +@@ -1541,6 +1541,7 @@ static int smb3_fs_context_parse_param(struct fs_context *fc, + + cifs_parse_mount_err: + kfree_sensitive(ctx->password); ++ ctx->password = NULL; + return -EINVAL; + } + +-- +cgit 1.2.3-korg diff --git a/tests/cases/CVE-2023-6111.patch b/tests/cases/CVE-2023-6111.patch new file mode 100644 index 0000000..716db2b --- /dev/null +++ b/tests/cases/CVE-2023-6111.patch @@ -0,0 +1,75 @@ +From 93995bf4af2c5a99e2a87f0cd5ce547d31eb7630 Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Mon, 6 Nov 2023 10:53:09 +0100 +Subject: netfilter: nf_tables: remove catchall element in GC sync path + +The expired catchall element is not deactivated and removed from GC sync +path. This path holds mutex so just call nft_setelem_data_deactivate() +and nft_setelem_catchall_remove() before queueing the GC work. + +Fixes: 4a9e12ea7e70 ("netfilter: nft_set_pipapo: call nft_trans_gc_queue_sync() in catchall GC") +Reported-by: lonial con +Signed-off-by: Pablo Neira Ayuso +--- + net/netfilter/nf_tables_api.c | 22 +++++++++++++++++----- + 1 file changed, 17 insertions(+), 5 deletions(-) + +diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c +index 146b7447a9692d..a761ee6796f6fa 100644 +--- a/net/netfilter/nf_tables_api.c ++++ b/net/netfilter/nf_tables_api.c +@@ -6520,6 +6520,12 @@ static int nft_setelem_deactivate(const struct net *net, + return ret; + } + ++static void nft_setelem_catchall_destroy(struct nft_set_elem_catchall *catchall) ++{ ++ list_del_rcu(&catchall->list); ++ kfree_rcu(catchall, rcu); ++} ++ + static void nft_setelem_catchall_remove(const struct net *net, + const struct nft_set *set, + struct nft_elem_priv *elem_priv) +@@ -6528,8 +6534,7 @@ static void nft_setelem_catchall_remove(const struct net *net, + + list_for_each_entry_safe(catchall, next, &set->catchall_list, list) { + if (catchall->elem == elem_priv) { +- list_del_rcu(&catchall->list); +- kfree_rcu(catchall, rcu); ++ nft_setelem_catchall_destroy(catchall); + break; + } + } +@@ -9678,11 +9683,12 @@ static struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc, + unsigned int gc_seq, + bool sync) + { +- struct nft_set_elem_catchall *catchall; ++ struct nft_set_elem_catchall *catchall, *next; + const struct nft_set *set = gc->set; ++ struct nft_elem_priv *elem_priv; + struct nft_set_ext *ext; + +- list_for_each_entry_rcu(catchall, &set->catchall_list, list) { ++ list_for_each_entry_safe(catchall, next, &set->catchall_list, list) { + ext = nft_set_elem_ext(set, catchall->elem); + + if (!nft_set_elem_expired(ext)) +@@ -9700,7 +9706,13 @@ dead_elem: + if (!gc) + return NULL; + +- nft_trans_gc_elem_add(gc, catchall->elem); ++ elem_priv = catchall->elem; ++ if (sync) { ++ nft_setelem_data_deactivate(gc->net, gc->set, elem_priv); ++ nft_setelem_catchall_destroy(catchall); ++ } ++ ++ nft_trans_gc_elem_add(gc, elem_priv); + } + + return gc; +-- +cgit 1.2.3-korg