From 158b3ce973c1130ee91c5a80060017e6a6c41cc0 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 19 Dec 2022 19:58:48 +0000 Subject: [PATCH 01/12] update to xen-4.16.3 --- .gitignore | 2 +- sources | 2 +- xen.canonicalize.patch | 4 +- xen.spec | 128 +-- xsa326-4.16-oxenstored-01.patch | 55 -- xsa326-4.16-oxenstored-02.patch | 94 -- xsa326-4.16-oxenstored-03.patch | 119 --- xsa326-4.16-oxenstored-04.patch | 85 -- xsa326-4.16-oxenstored-05.patch | 214 ----- xsa326-4.16-oxenstored-06.patch | 127 --- xsa326-4.16-oxenstored-07.patch | 872 ------------------ xsa326-4.16-oxenstored-08.patch | 49 - xsa326-4.16-xenstored-01.patch | 205 ---- xsa326-4.16-xenstored-02.patch | 108 --- xsa326-4.16-xenstored-03.patch | 192 ---- xsa326-4.16-xenstored-04.patch | 302 ------ xsa326-4.16-xenstored-05.patch | 443 --------- xsa326-4.16-xenstored-06.patch | 85 -- xsa326-4.16-xenstored-07.patch | 53 -- xsa326-4.16-xenstored-08.patch | 326 ------- xsa326-4.16-xenstored-09.patch | 244 ----- xsa326-4.16-xenstored-10.patch | 88 -- xsa326-4.16-xenstored-11.patch | 280 ------ xsa326-4.16-xenstored-12.patch | 76 -- xsa326-4.16-xenstored-13.patch | 89 -- xsa326-4.16-xenstored-14.patch | 334 ------- xsa326-4.16-xenstored-15.patch | 54 -- xsa326-4.16-xenstored-16.patch | 238 ----- ...-arch-specific-default-paging-memory.patch | 147 --- ...struct-the-P2M-pages-pool-for-guests.patch | 187 ---- ...plement-XEN_DOMCTL_shadow_op-for-Arm.patch | 106 --- ...-and-free-P2M-pages-from-the-P2M-poo.patch | 287 ------ xsa410-4.16-01.patch | 59 -- xsa410-4.16-02.patch | 165 ---- xsa410-4.16-03.patch | 113 --- xsa410-4.16-04.patch | 62 -- xsa410-4.16-05.patch | 60 -- xsa410-4.16-06.patch | 255 ----- xsa410-4.16-07.patch | 82 -- xsa410-4.16-08.patch | 96 -- xsa410-4.16-09.patch | 159 ---- xsa410-4.16-10.patch | 171 ---- xsa411.patch | 55 -- xsa412-4.16.patch | 245 ----- xsa414.patch | 112 --- xsa415.patch | 134 --- xsa416-4.16.patch | 704 -------------- xsa417.patch | 135 --- xsa418-4.16-01.patch | 119 --- xsa418-4.16-02.patch | 103 --- xsa418-4.16-03.patch | 243 ----- xsa418-4.16-04.patch | 108 --- xsa418-4.16-05.patch | 164 ---- xsa418-4.16-06.patch | 174 ---- xsa418-4.16-07.patch | 163 ---- xsa419-oxenstored.patch | 85 -- xsa419-xenstored-01.patch | 289 ------ xsa419-xenstored-02.patch | 93 -- xsa419-xenstored-03.patch | 44 - xsa420.patch | 68 -- xsa421-01.patch | 40 - xsa421-02.patch | 401 -------- xsa422-4.16-1.patch | 70 -- xsa422-4.16-2.patch | 100 -- 64 files changed, 10 insertions(+), 10456 deletions(-) delete mode 100644 xsa326-4.16-oxenstored-01.patch delete mode 100644 xsa326-4.16-oxenstored-02.patch delete mode 100644 xsa326-4.16-oxenstored-03.patch delete mode 100644 xsa326-4.16-oxenstored-04.patch delete mode 100644 xsa326-4.16-oxenstored-05.patch delete mode 100644 xsa326-4.16-oxenstored-06.patch delete mode 100644 xsa326-4.16-oxenstored-07.patch delete mode 100644 xsa326-4.16-oxenstored-08.patch delete mode 100644 xsa326-4.16-xenstored-01.patch delete mode 100644 xsa326-4.16-xenstored-02.patch delete mode 100644 xsa326-4.16-xenstored-03.patch delete mode 100644 xsa326-4.16-xenstored-04.patch delete mode 100644 xsa326-4.16-xenstored-05.patch delete mode 100644 xsa326-4.16-xenstored-06.patch delete mode 100644 xsa326-4.16-xenstored-07.patch delete mode 100644 xsa326-4.16-xenstored-08.patch delete mode 100644 xsa326-4.16-xenstored-09.patch delete mode 100644 xsa326-4.16-xenstored-10.patch delete mode 100644 xsa326-4.16-xenstored-11.patch delete mode 100644 xsa326-4.16-xenstored-12.patch delete mode 100644 xsa326-4.16-xenstored-13.patch delete mode 100644 xsa326-4.16-xenstored-14.patch delete mode 100644 xsa326-4.16-xenstored-15.patch delete mode 100644 xsa326-4.16-xenstored-16.patch delete mode 100644 xsa409-4.13-0001-libxl-docs-Use-arch-specific-default-paging-memory.patch delete mode 100644 xsa409-4.13-0002-xen-arm-Construct-the-P2M-pages-pool-for-guests.patch delete mode 100644 xsa409-4.13-0003-xen-arm-libxl-Implement-XEN_DOMCTL_shadow_op-for-Arm.patch delete mode 100644 xsa409-4.13-0004-xen-arm-Allocate-and-free-P2M-pages-from-the-P2M-poo.patch delete mode 100644 xsa410-4.16-01.patch delete mode 100644 xsa410-4.16-02.patch delete mode 100644 xsa410-4.16-03.patch delete mode 100644 xsa410-4.16-04.patch delete mode 100644 xsa410-4.16-05.patch delete mode 100644 xsa410-4.16-06.patch delete mode 100644 xsa410-4.16-07.patch delete mode 100644 xsa410-4.16-08.patch delete mode 100644 xsa410-4.16-09.patch delete mode 100644 xsa410-4.16-10.patch delete mode 100644 xsa411.patch delete mode 100644 xsa412-4.16.patch delete mode 100644 xsa414.patch delete mode 100644 xsa415.patch delete mode 100644 xsa416-4.16.patch delete mode 100644 xsa417.patch delete mode 100644 xsa418-4.16-01.patch delete mode 100644 xsa418-4.16-02.patch delete mode 100644 xsa418-4.16-03.patch delete mode 100644 xsa418-4.16-04.patch delete mode 100644 xsa418-4.16-05.patch delete mode 100644 xsa418-4.16-06.patch delete mode 100644 xsa418-4.16-07.patch delete mode 100644 xsa419-oxenstored.patch delete mode 100644 xsa419-xenstored-01.patch delete mode 100644 xsa419-xenstored-02.patch delete mode 100644 xsa419-xenstored-03.patch delete mode 100644 xsa420.patch delete mode 100644 xsa421-01.patch delete mode 100644 xsa421-02.patch delete mode 100644 xsa422-4.16-1.patch delete mode 100644 xsa422-4.16-2.patch diff --git a/.gitignore b/.gitignore index f41c7f1..3d1112b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.16.2.tar.gz +/xen-4.16.3.tar.gz diff --git a/sources b/sources index f6803eb..b3ba74d 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.16.2.tar.gz) = b6cd036c1073798dffa167ca14c954fbdfb4c0ef99662f7c435e7e5de687d1bde8856ff6bd030d0d2e661bd17ab631551f01b2cc728cad7e70b59aaa6e692783 +SHA512 (xen-4.16.3.tar.gz) = b8cbd6f95681de5f824ada2d3cbe0653a38514a18df0dafcf811fb255219c7abec96f46217bdb0c83e1119f685da9a6af7194eeaa94f1cc3c892702782133b4f diff --git a/xen.canonicalize.patch b/xen.canonicalize.patch index e339530..f3ae37d 100644 --- a/xen.canonicalize.patch +++ b/xen.canonicalize.patch @@ -13,8 +13,8 @@ if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) return EINVAL; -- node = canonicalize(conn, in, vec[0]); -+ node = xenstore_canonicalize(conn, in, vec[0]); +- node = canonicalize(conn, ctx, vec[0]); ++ node = xenstore_canonicalize(conn, ctx, vec[0]); if (!node) return ENOMEM; list_for_each_entry(watch, &conn->watches, list) { diff --git a/xen.spec b/xen.spec index 27ed461..f2f311e 100644 --- a/xen.spec +++ b/xen.spec @@ -54,8 +54,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.16.2 -Release: 4%{?dist} +Version: 4.16.3 +Release: 1%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -111,66 +111,6 @@ Patch43: xen.gcc11.fixes.patch Patch44: xsa376.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch -Patch47: xsa410-4.16-01.patch -Patch48: xsa410-4.16-02.patch -Patch49: xsa410-4.16-03.patch -Patch50: xsa410-4.16-04.patch -Patch51: xsa410-4.16-05.patch -Patch52: xsa410-4.16-06.patch -Patch53: xsa410-4.16-07.patch -Patch54: xsa410-4.16-08.patch -Patch55: xsa410-4.16-09.patch -Patch56: xsa410-4.16-10.patch -Patch57: xsa409-4.13-0001-libxl-docs-Use-arch-specific-default-paging-memory.patch -Patch58: xsa409-4.13-0002-xen-arm-Construct-the-P2M-pages-pool-for-guests.patch -Patch59: xsa409-4.13-0003-xen-arm-libxl-Implement-XEN_DOMCTL_shadow_op-for-Arm.patch -Patch60: xsa409-4.13-0004-xen-arm-Allocate-and-free-P2M-pages-from-the-P2M-poo.patch -Patch61: xsa411.patch -Patch62: xsa412-4.16.patch -Patch63: xsa414.patch -Patch64: xsa415.patch -Patch65: xsa326-4.16-oxenstored-01.patch -Patch66: xsa326-4.16-oxenstored-02.patch -Patch67: xsa326-4.16-oxenstored-03.patch -Patch68: xsa326-4.16-oxenstored-04.patch -Patch69: xsa326-4.16-oxenstored-05.patch -Patch70: xsa326-4.16-oxenstored-06.patch -Patch71: xsa326-4.16-oxenstored-07.patch -Patch72: xsa326-4.16-oxenstored-08.patch -Patch73: xsa326-4.16-xenstored-01.patch -Patch74: xsa326-4.16-xenstored-02.patch -Patch75: xsa326-4.16-xenstored-03.patch -Patch76: xsa326-4.16-xenstored-04.patch -Patch77: xsa326-4.16-xenstored-05.patch -Patch78: xsa326-4.16-xenstored-06.patch -Patch79: xsa326-4.16-xenstored-07.patch -Patch80: xsa326-4.16-xenstored-08.patch -Patch81: xsa326-4.16-xenstored-09.patch -Patch82: xsa326-4.16-xenstored-10.patch -Patch83: xsa326-4.16-xenstored-11.patch -Patch84: xsa326-4.16-xenstored-12.patch -Patch85: xsa326-4.16-xenstored-13.patch -Patch86: xsa326-4.16-xenstored-14.patch -Patch87: xsa326-4.16-xenstored-15.patch -Patch88: xsa326-4.16-xenstored-16.patch -Patch89: xsa416-4.16.patch -Patch90: xsa417.patch -Patch91: xsa418-4.16-01.patch -Patch92: xsa418-4.16-02.patch -Patch93: xsa418-4.16-03.patch -Patch94: xsa418-4.16-04.patch -Patch95: xsa418-4.16-05.patch -Patch96: xsa418-4.16-06.patch -Patch97: xsa418-4.16-07.patch -Patch98: xsa419-oxenstored.patch -Patch99: xsa419-xenstored-01.patch -Patch100: xsa419-xenstored-02.patch -Patch101: xsa419-xenstored-03.patch -Patch102: xsa420.patch -Patch103: xsa421-01.patch -Patch104: xsa421-02.patch -Patch105: xsa422-4.16-1.patch -Patch106: xsa422-4.16-2.patch %if %build_qemutrad @@ -383,66 +323,6 @@ manage Xen virtual machines. %patch44 -p1 %patch45 -p1 %patch46 -p1 -%patch47 -p1 -%patch48 -p1 -%patch49 -p1 -%patch50 -p1 -%patch51 -p1 -%patch52 -p1 -%patch53 -p1 -%patch54 -p1 -%patch55 -p1 -%patch56 -p1 -%patch57 -p1 -%patch58 -p1 -%patch59 -p1 -%patch60 -p1 -%patch61 -p1 -%patch62 -p1 -%patch63 -p1 -%patch64 -p1 -%patch65 -p1 -%patch66 -p1 -%patch67 -p1 -%patch68 -p1 -%patch69 -p1 -%patch70 -p1 -%patch71 -p1 -%patch72 -p1 -%patch73 -p1 -%patch74 -p1 -%patch75 -p1 -%patch76 -p1 -%patch77 -p1 -%patch78 -p1 -%patch79 -p1 -%patch80 -p1 -%patch81 -p1 -%patch82 -p1 -%patch83 -p1 -%patch84 -p1 -%patch85 -p1 -%patch86 -p1 -%patch87 -p1 -%patch88 -p1 -%patch89 -p1 -%patch90 -p1 -%patch91 -p1 -%patch92 -p1 -%patch93 -p1 -%patch94 -p1 -%patch95 -p1 -%patch96 -p1 -%patch97 -p1 -%patch98 -p1 -%patch99 -p1 -%patch100 -p1 -%patch101 -p1 -%patch102 -p1 -%patch103 -p1 -%patch104 -p1 -%patch105 -p1 -%patch106 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -1058,6 +938,10 @@ fi %endif %changelog +* Mon Dec 19 2022 Michael Young - 4.16.3-1 +- update to xen-4.16.3 + remove or adjust patches now included or superceded upstream + * Tue Nov 08 2022 Michael Young - 4.16.2-4 - x86: Multiple speculative security issues [XSA-422, CVE-2022-23824] diff --git a/xsa326-4.16-oxenstored-01.patch b/xsa326-4.16-oxenstored-01.patch deleted file mode 100644 index 848a5d0..0000000 --- a/xsa326-4.16-oxenstored-01.patch +++ /dev/null @@ -1,55 +0,0 @@ -From 8d6bb4ac40619877130533b11655829101b31d04 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Date: Wed, 12 Oct 2022 19:13:01 +0100 -Subject: tools/ocaml/xenstored: Synchronise defaults with oxenstore.conf.in -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -We currently have 2 different set of defaults in upstream Xen git tree: -* defined in the source code, only used if there is no config file -* defined in the oxenstored.conf.in upstream Xen - -An oxenstored.conf file is not mandatory, and if missing, maxrequests in -particular has an unsafe default. - -Resync the defaults from oxenstored.conf.in into the source code. - -This is part of XSA-326 / CVE-2022-42316. - -Reported-by: Julien Grall -Signed-off-by: Edwin Török -Acked-by: Christian Lindig - -diff --git a/tools/ocaml/xenstored/define.ml b/tools/ocaml/xenstored/define.ml -index ebe18b8e312c..6b06f808595b 100644 ---- a/tools/ocaml/xenstored/define.ml -+++ b/tools/ocaml/xenstored/define.ml -@@ -21,9 +21,9 @@ let xs_daemon_socket = Paths.xen_run_stored ^ "/socket" - - let default_config_dir = Paths.xen_config_dir - --let maxwatch = ref (50) --let maxtransaction = ref (20) --let maxrequests = ref (-1) (* maximum requests per transaction *) -+let maxwatch = ref (100) -+let maxtransaction = ref (10) -+let maxrequests = ref (1024) (* maximum requests per transaction *) - - let conflict_burst_limit = ref 5.0 - let conflict_max_history_seconds = ref 0.05 -diff --git a/tools/ocaml/xenstored/quota.ml b/tools/ocaml/xenstored/quota.ml -index abcac912805a..6e3d6401ae89 100644 ---- a/tools/ocaml/xenstored/quota.ml -+++ b/tools/ocaml/xenstored/quota.ml -@@ -20,8 +20,8 @@ exception Transaction_opened - - let warn fmt = Logging.warn "quota" fmt - let activate = ref true --let maxent = ref (10000) --let maxsize = ref (4096) -+let maxent = ref (1000) -+let maxsize = ref (2048) - - type t = { - maxent: int; (* max entities per domU *) diff --git a/xsa326-4.16-oxenstored-02.patch b/xsa326-4.16-oxenstored-02.patch deleted file mode 100644 index 7680efb..0000000 --- a/xsa326-4.16-oxenstored-02.patch +++ /dev/null @@ -1,94 +0,0 @@ -From 78d5af44ab13bb18c87b6ad75e505bd374379cb3 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Date: Thu, 28 Jul 2022 17:08:15 +0100 -Subject: tools/ocaml/xenstored: Check for maxrequests before performing - operations -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Previously we'd perform the operation, record the updated tree in the -transaction record, then try to insert a watchop path and the reply packet. - -If we exceeded max requests we would've returned EQUOTA, but still: -* have performed the operation on the transaction's tree -* have recorded the watchop, making this queue effectively unbounded - -It is better if we check whether we'd have room to store the operation before -performing the transaction, and raise EQUOTA there. Then the transaction -record won't grow. - -This is part of XSA-326 / CVE-2022-42317. - -Reported-by: Julien Grall -Signed-off-by: Edwin Török -Acked-by: Christian Lindig - -diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml -index 27790d4a5c41..dd58e6979cf9 100644 ---- a/tools/ocaml/xenstored/process.ml -+++ b/tools/ocaml/xenstored/process.ml -@@ -389,6 +389,7 @@ let input_handle_error ~cons ~doms ~fct ~con ~t ~req = - let reply_error e = - Packet.Error e in - try -+ Transaction.check_quota_exn ~perm:(Connection.get_perm con) t; - fct con t doms cons req.Packet.data - with - | Define.Invalid_path -> reply_error "EINVAL" -@@ -681,9 +682,10 @@ let process_packet ~store ~cons ~doms ~con ~req = - in - - let response = try -+ Transaction.check_quota_exn ~perm:(Connection.get_perm con) t; - if tid <> Transaction.none then - (* Remember the request and response for this operation in case we need to replay the transaction *) -- Transaction.add_operation ~perm:(Connection.get_perm con) t req response; -+ Transaction.add_operation t req response; - response - with Quota.Limit_reached -> - Packet.Error "EQUOTA" -diff --git a/tools/ocaml/xenstored/transaction.ml b/tools/ocaml/xenstored/transaction.ml -index 17b1bdf2eaf9..294143e2335b 100644 ---- a/tools/ocaml/xenstored/transaction.ml -+++ b/tools/ocaml/xenstored/transaction.ml -@@ -85,6 +85,7 @@ type t = { - oldroot: Store.Node.t; - mutable paths: (Xenbus.Xb.Op.operation * Store.Path.t) list; - mutable operations: (Packet.request * Packet.response) list; -+ mutable quota_reached: bool; - mutable read_lowpath: Store.Path.t option; - mutable write_lowpath: Store.Path.t option; - } -@@ -127,6 +128,7 @@ let make ?(internal=false) id store = - oldroot = Store.get_root store; - paths = []; - operations = []; -+ quota_reached = false; - read_lowpath = None; - write_lowpath = None; - } in -@@ -143,13 +145,19 @@ let get_root t = Store.get_root t.store - - let is_read_only t = t.paths = [] - let add_wop t ty path = t.paths <- (ty, path) :: t.paths --let add_operation ~perm t request response = -+let get_operations t = List.rev t.operations -+ -+let check_quota_exn ~perm t = - if !Define.maxrequests >= 0 - && not (Perms.Connection.is_dom0 perm) -- && List.length t.operations >= !Define.maxrequests -- then raise Quota.Limit_reached; -+ && (t.quota_reached || List.length t.operations >= !Define.maxrequests) -+ then begin -+ t.quota_reached <- true; -+ raise Quota.Limit_reached; -+ end -+ -+let add_operation t request response = - t.operations <- (request, response) :: t.operations --let get_operations t = List.rev t.operations - let set_read_lowpath t path = t.read_lowpath <- get_lowest path t.read_lowpath - let set_write_lowpath t path = t.write_lowpath <- get_lowest path t.write_lowpath - diff --git a/xsa326-4.16-oxenstored-03.patch b/xsa326-4.16-oxenstored-03.patch deleted file mode 100644 index 0b42411..0000000 --- a/xsa326-4.16-oxenstored-03.patch +++ /dev/null @@ -1,119 +0,0 @@ -From 600c45e49c2060e077c06ab19078da89aa8e2e08 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Date: Wed, 12 Oct 2022 19:13:07 +0100 -Subject: tools/ocaml: GC parameter tuning -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -By default the OCaml garbage collector would return memory to the OS only -after unused memory is 5x live memory. Tweak this to 120% instead, which -would match the major GC speed. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Edwin Török -Acked-by: Christian Lindig - -diff --git a/tools/ocaml/xenstored/define.ml b/tools/ocaml/xenstored/define.ml -index 6b06f808595b..ba63a8147e09 100644 ---- a/tools/ocaml/xenstored/define.ml -+++ b/tools/ocaml/xenstored/define.ml -@@ -25,6 +25,7 @@ let maxwatch = ref (100) - let maxtransaction = ref (10) - let maxrequests = ref (1024) (* maximum requests per transaction *) - -+let gc_max_overhead = ref 120 (* 120% see comment in xenstored.ml *) - let conflict_burst_limit = ref 5.0 - let conflict_max_history_seconds = ref 0.05 - let conflict_rate_limit_is_aggregate = ref true -diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml -index d44ae673c42a..3b57ad016dfb 100644 ---- a/tools/ocaml/xenstored/xenstored.ml -+++ b/tools/ocaml/xenstored/xenstored.ml -@@ -104,6 +104,7 @@ let parse_config filename = - ("quota-maxsize", Config.Set_int Quota.maxsize); - ("quota-maxrequests", Config.Set_int Define.maxrequests); - ("quota-path-max", Config.Set_int Define.path_max); -+ ("gc-max-overhead", Config.Set_int Define.gc_max_overhead); - ("test-eagain", Config.Set_bool Transaction.test_eagain); - ("persistent", Config.Set_bool Disk.enable); - ("xenstored-log-file", Config.String Logging.set_xenstored_log_destination); -@@ -265,6 +266,67 @@ let to_file store cons fds file = - (fun () -> close_out channel) - end - -+(* -+ By default OCaml's GC only returns memory to the OS when it exceeds a -+ configurable 'max overhead' setting. -+ The default is 500%, that is 5/6th of the OCaml heap needs to be free -+ and only 1/6th live for a compaction to be triggerred that would -+ release memory back to the OS. -+ If the limit is not hit then the OCaml process can reuse that memory -+ for its own purposes, but other processes won't be able to use it. -+ -+ There is also a 'space overhead' setting that controls how much work -+ each major GC slice does, and by default aims at having no more than -+ 80% or 120% (depending on version) garbage values compared to live -+ values. -+ This doesn't have as much relevance to memory returned to the OS as -+ long as space_overhead <= max_overhead, because compaction is only -+ triggerred at the end of major GC cycles. -+ -+ The defaults are too large once the program starts using ~100MiB of -+ memory, at which point ~500MiB would be unavailable to other processes -+ (which would be fine if this was the main process in this VM, but it is -+ not). -+ -+ Max overhead can also be set to 0, however this is for testing purposes -+ only (setting it lower than 'space overhead' wouldn't help because the -+ major GC wouldn't run fast enough, and compaction does have a -+ performance cost: we can only compact contiguous regions, so memory has -+ to be moved around). -+ -+ Max overhead controls how often the heap is compacted, which is useful -+ if there are burst of activity followed by long periods of idle state, -+ or if a domain quits, etc. Compaction returns memory to the OS. -+ -+ wasted = live * space_overhead / 100 -+ -+ For globally overriding the GC settings one can use OCAMLRUNPARAM, -+ however we provide a config file override to be consistent with other -+ oxenstored settings. -+ -+ One might want to dynamically adjust the overhead setting based on used -+ memory, i.e. to use a fixed upper bound in bytes, not percentage. However -+ measurements show that such adjustments increase GC overhead massively, -+ while still not guaranteeing that memory is returned any more quickly -+ than with a percentage based setting. -+ -+ The allocation policy could also be tweaked, e.g. first fit would reduce -+ fragmentation and thus memory usage, but the documentation warns that it -+ can be sensibly slower, and indeed one of our own testcases can trigger -+ such a corner case where it is multiple times slower, so it is best to keep -+ the default allocation policy (next-fit/best-fit depending on version). -+ -+ There are other tweaks that can be attempted in the future, e.g. setting -+ 'ulimit -v' to 75% of RAM, however getting the kernel to actually return -+ NULL from allocations is difficult even with that setting, and without a -+ NULL the emergency GC won't be triggerred. -+ Perhaps cgroup limits could help, but for now tweak the safest only. -+*) -+ -+let tweak_gc () = -+ Gc.set { (Gc.get ()) with Gc.max_overhead = !Define.gc_max_overhead } -+ -+ - let _ = - let cf = do_argv in - let pidfile = -@@ -274,6 +336,8 @@ let _ = - default_pidfile - in - -+ tweak_gc (); -+ - (try - Unixext.mkdir_rec (Filename.dirname pidfile) 0o755 - with _ -> diff --git a/xsa326-4.16-oxenstored-04.patch b/xsa326-4.16-oxenstored-04.patch deleted file mode 100644 index 0f2534b..0000000 --- a/xsa326-4.16-oxenstored-04.patch +++ /dev/null @@ -1,85 +0,0 @@ -From fd6d9cd3d20e496bdbf3e0a07354f65de0bcf4ae Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Date: Fri, 29 Jul 2022 18:53:29 +0100 -Subject: tools/ocaml/libs/xb: hide type of Xb.t -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Hiding the type will make it easier to change the implementation -in the future without breaking code that relies on it. - -No functional change. - -Signed-off-by: Edwin Török -Acked-by: Christian Lindig -(cherry picked from commit 7ade30a1451734d041363c750a65d322e25b47ba) - -Reported-by: Julien Grall -diff --git a/tools/ocaml/libs/xb/xb.ml b/tools/ocaml/libs/xb/xb.ml -index 104d319d7747..8404ddd8a682 100644 ---- a/tools/ocaml/libs/xb/xb.ml -+++ b/tools/ocaml/libs/xb/xb.ml -@@ -196,6 +196,9 @@ let peek_output con = Queue.peek con.pkt_out - let input_len con = Queue.length con.pkt_in - let has_in_packet con = Queue.length con.pkt_in > 0 - let get_in_packet con = Queue.pop con.pkt_in -+let has_partial_input con = match con.partial_in with -+ | HaveHdr _ -> true -+ | NoHdr (n, _) -> n < Partial.header_size () - let has_more_input con = - match con.backend with - | Fd _ -> false -diff --git a/tools/ocaml/libs/xb/xb.mli b/tools/ocaml/libs/xb/xb.mli -index 3a00da6cddc1..794e35bb343e 100644 ---- a/tools/ocaml/libs/xb/xb.mli -+++ b/tools/ocaml/libs/xb/xb.mli -@@ -66,13 +66,7 @@ type backend_mmap = { - type backend_fd = { fd : Unix.file_descr; } - type backend = Fd of backend_fd | Xenmmap of backend_mmap - type partial_buf = HaveHdr of Partial.pkt | NoHdr of int * bytes --type t = { -- backend : backend; -- pkt_in : Packet.t Queue.t; -- pkt_out : Packet.t Queue.t; -- mutable partial_in : partial_buf; -- mutable partial_out : string; --} -+type t - val init_partial_in : unit -> partial_buf - val reconnect : t -> unit - val queue : t -> Packet.t -> unit -@@ -97,6 +91,7 @@ val has_output : t -> bool - val peek_output : t -> Packet.t - val input_len : t -> int - val has_in_packet : t -> bool -+val has_partial_input : t -> bool - val get_in_packet : t -> Packet.t - val has_more_input : t -> bool - val is_selectable : t -> bool -diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/connection.ml -index 65f99ea6f28a..38b47363a173 100644 ---- a/tools/ocaml/xenstored/connection.ml -+++ b/tools/ocaml/xenstored/connection.ml -@@ -125,9 +125,7 @@ let get_perm con = - let set_target con target_domid = - con.perm <- Perms.Connection.set_target (get_perm con) ~perms:[Perms.READ; Perms.WRITE] target_domid - --let is_backend_mmap con = match con.xb.Xenbus.Xb.backend with -- | Xenbus.Xb.Xenmmap _ -> true -- | _ -> false -+let is_backend_mmap con = Xenbus.Xb.is_mmap con.xb - - let send_reply con tid rid ty data = - if (String.length data) > xenstore_payload_max && (is_backend_mmap con) then -@@ -280,9 +278,7 @@ let get_transaction con tid = - - let do_input con = Xenbus.Xb.input con.xb - let has_input con = Xenbus.Xb.has_in_packet con.xb --let has_partial_input con = match con.xb.Xenbus.Xb.partial_in with -- | HaveHdr _ -> true -- | NoHdr (n, _) -> n < Xenbus.Partial.header_size () -+let has_partial_input con = Xenbus.Xb.has_partial_input con.xb - let pop_in con = Xenbus.Xb.get_in_packet con.xb - let has_more_input con = Xenbus.Xb.has_more_input con.xb - diff --git a/xsa326-4.16-oxenstored-05.patch b/xsa326-4.16-oxenstored-05.patch deleted file mode 100644 index 8b253a9..0000000 --- a/xsa326-4.16-oxenstored-05.patch +++ /dev/null @@ -1,214 +0,0 @@ -From f13fe5903361953e4ccf8602b9c8df7e64568d55 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Date: Wed, 12 Oct 2022 19:13:02 +0100 -Subject: tools/ocaml: Change Xb.input to return Packet.t option -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The queue here would only ever hold at most one element. This will simplify -follow-up patches. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Edwin Török -Acked-by: Christian Lindig - -diff --git a/tools/ocaml/libs/xb/xb.ml b/tools/ocaml/libs/xb/xb.ml -index 8404ddd8a682..165fd4a1edf4 100644 ---- a/tools/ocaml/libs/xb/xb.ml -+++ b/tools/ocaml/libs/xb/xb.ml -@@ -45,7 +45,6 @@ type partial_buf = HaveHdr of Partial.pkt | NoHdr of int * bytes - type t = - { - backend: backend; -- pkt_in: Packet.t Queue.t; - pkt_out: Packet.t Queue.t; - mutable partial_in: partial_buf; - mutable partial_out: string; -@@ -62,7 +61,6 @@ let reconnect t = match t.backend with - Xs_ring.close backend.mmap; - backend.eventchn_notify (); - (* Clear our old connection state *) -- Queue.clear t.pkt_in; - Queue.clear t.pkt_out; - t.partial_in <- init_partial_in (); - t.partial_out <- "" -@@ -124,7 +122,6 @@ let output con = - - (* NB: can throw Reconnect *) - let input con = -- let newpacket = ref false in - let to_read = - match con.partial_in with - | HaveHdr partial_pkt -> Partial.to_complete partial_pkt -@@ -143,21 +140,19 @@ let input con = - if Partial.to_complete partial_pkt = 0 then ( - let pkt = Packet.of_partialpkt partial_pkt in - con.partial_in <- init_partial_in (); -- Queue.push pkt con.pkt_in; -- newpacket := true -- ) -+ Some pkt -+ ) else None - | NoHdr (i, buf) -> - (* we complete the partial header *) - if sz > 0 then - Bytes.blit b 0 buf (Partial.header_size () - i) sz; - con.partial_in <- if sz = i then -- HaveHdr (Partial.of_string (Bytes.to_string buf)) else NoHdr (i - sz, buf) -- ); -- !newpacket -+ HaveHdr (Partial.of_string (Bytes.to_string buf)) else NoHdr (i - sz, buf); -+ None -+ ) - - let newcon backend = { - backend = backend; -- pkt_in = Queue.create (); - pkt_out = Queue.create (); - partial_in = init_partial_in (); - partial_out = ""; -@@ -193,9 +188,6 @@ let has_output con = has_new_output con || has_old_output con - - let peek_output con = Queue.peek con.pkt_out - --let input_len con = Queue.length con.pkt_in --let has_in_packet con = Queue.length con.pkt_in > 0 --let get_in_packet con = Queue.pop con.pkt_in - let has_partial_input con = match con.partial_in with - | HaveHdr _ -> true - | NoHdr (n, _) -> n < Partial.header_size () -diff --git a/tools/ocaml/libs/xb/xb.mli b/tools/ocaml/libs/xb/xb.mli -index 794e35bb343e..91c682162cea 100644 ---- a/tools/ocaml/libs/xb/xb.mli -+++ b/tools/ocaml/libs/xb/xb.mli -@@ -77,7 +77,7 @@ val write_fd : backend_fd -> 'a -> string -> int -> int - val write_mmap : backend_mmap -> 'a -> string -> int -> int - val write : t -> string -> int -> int - val output : t -> bool --val input : t -> bool -+val input : t -> Packet.t option - val newcon : backend -> t - val open_fd : Unix.file_descr -> t - val open_mmap : Xenmmap.mmap_interface -> (unit -> unit) -> t -@@ -89,10 +89,7 @@ val has_new_output : t -> bool - val has_old_output : t -> bool - val has_output : t -> bool - val peek_output : t -> Packet.t --val input_len : t -> int --val has_in_packet : t -> bool - val has_partial_input : t -> bool --val get_in_packet : t -> Packet.t - val has_more_input : t -> bool - val is_selectable : t -> bool - val get_fd : t -> Unix.file_descr -diff --git a/tools/ocaml/libs/xs/xsraw.ml b/tools/ocaml/libs/xs/xsraw.ml -index d982fb24dbb1..451f8b38dbcc 100644 ---- a/tools/ocaml/libs/xs/xsraw.ml -+++ b/tools/ocaml/libs/xs/xsraw.ml -@@ -94,26 +94,18 @@ let pkt_send con = - done - - (* receive one packet - can sleep *) --let pkt_recv con = -- let workdone = ref false in -- while not !workdone -- do -- workdone := Xb.input con.xb -- done; -- Xb.get_in_packet con.xb -+let rec pkt_recv con = -+ match Xb.input con.xb with -+ | Some packet -> packet -+ | None -> pkt_recv con - - let pkt_recv_timeout con timeout = - let fd = Xb.get_fd con.xb in - let r, _, _ = Unix.select [ fd ] [] [] timeout in - if r = [] then - true, None -- else ( -- let workdone = Xb.input con.xb in -- if workdone then -- false, (Some (Xb.get_in_packet con.xb)) -- else -- false, None -- ) -+ else -+ false, Xb.input con.xb - - let queue_watchevent con data = - let ls = split_string ~limit:2 '\000' data in -diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/connection.ml -index 38b47363a173..cc20e047d2b9 100644 ---- a/tools/ocaml/xenstored/connection.ml -+++ b/tools/ocaml/xenstored/connection.ml -@@ -277,9 +277,7 @@ let get_transaction con tid = - Hashtbl.find con.transactions tid - - let do_input con = Xenbus.Xb.input con.xb --let has_input con = Xenbus.Xb.has_in_packet con.xb - let has_partial_input con = Xenbus.Xb.has_partial_input con.xb --let pop_in con = Xenbus.Xb.get_in_packet con.xb - let has_more_input con = Xenbus.Xb.has_more_input con.xb - - let has_output con = Xenbus.Xb.has_output con.xb -@@ -307,7 +305,7 @@ let is_bad con = match con.dom with None -> false | Some dom -> Domain.is_bad_do - Restrictions below can be relaxed once xenstored learns to dump more - of its live state in a safe way *) - let has_extra_connection_data con = -- let has_in = has_input con || has_partial_input con in -+ let has_in = has_partial_input con in - let has_out = has_output con in - let has_socket = con.dom = None in - let has_nondefault_perms = make_perm con.dom <> con.perm in -diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml -index 6a3435c265d3..2d67456a2aa0 100644 ---- a/tools/ocaml/xenstored/process.ml -+++ b/tools/ocaml/xenstored/process.ml -@@ -195,10 +195,9 @@ let parse_live_update args = - | _ when Unix.gettimeofday () < t.deadline -> false - | l -> - warn "timeout reached: have to wait, migrate or shutdown %d domains:" (List.length l); -- let msgs = List.rev_map (fun con -> Printf.sprintf "%s: %d tx, in: %b, out: %b, perm: %s" -+ let msgs = List.rev_map (fun con -> Printf.sprintf "%s: %d tx, out: %b, perm: %s" - (Connection.get_domstr con) - (Connection.number_of_transactions con) -- (Connection.has_input con) - (Connection.has_output con) - (Connection.get_perm con |> Perms.Connection.to_string) - ) l in -@@ -705,16 +704,17 @@ let do_input store cons doms con = - info "%s requests a reconnect" (Connection.get_domstr con); - History.reconnect con; - info "%s reconnection complete" (Connection.get_domstr con); -- false -+ None - | Failure exp -> - error "caught exception %s" exp; - error "got a bad client %s" (sprintf "%-8s" (Connection.get_domstr con)); - Connection.mark_as_bad con; -- false -+ None - in - -- if newpacket then ( -- let packet = Connection.pop_in con in -+ match newpacket with -+ | None -> () -+ | Some packet -> - let tid, rid, ty, data = Xenbus.Xb.Packet.unpack packet in - let req = {Packet.tid=tid; Packet.rid=rid; Packet.ty=ty; Packet.data=data} in - -@@ -724,8 +724,7 @@ let do_input store cons doms con = - (Xenbus.Xb.Op.to_string ty) (sanitize_data data); *) - process_packet ~store ~cons ~doms ~con ~req; - write_access_log ~ty ~tid ~con:(Connection.get_domstr con) ~data; -- Connection.incr_ops con; -- ) -+ Connection.incr_ops con - - let do_output _store _cons _doms con = - if Connection.has_output con then ( diff --git a/xsa326-4.16-oxenstored-06.patch b/xsa326-4.16-oxenstored-06.patch deleted file mode 100644 index c8ebc34..0000000 --- a/xsa326-4.16-oxenstored-06.patch +++ /dev/null @@ -1,127 +0,0 @@ -From 2440a8b69a118fe14e73eb6cab4a050922866f1a Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Date: Wed, 12 Oct 2022 19:13:03 +0100 -Subject: tools/ocaml/xb: Add BoundedQueue -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Ensures we cannot store more than [capacity] elements in a [Queue]. Replacing -all Queue with this module will then ensure at compile time that all Queues -are correctly bound checked. - -Each element in the queue has a class with its own limits. This, in a -subsequent change, will ensure that command responses can proceed during a -flood of watch events. - -No functional change. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Edwin Török -Acked-by: Christian Lindig - -diff --git a/tools/ocaml/libs/xb/xb.ml b/tools/ocaml/libs/xb/xb.ml -index 165fd4a1edf4..4197a3888a68 100644 ---- a/tools/ocaml/libs/xb/xb.ml -+++ b/tools/ocaml/libs/xb/xb.ml -@@ -17,6 +17,98 @@ - module Op = struct include Op end - module Packet = struct include Packet end - -+module BoundedQueue : sig -+ type ('a, 'b) t -+ -+ (** [create ~capacity ~classify ~limit] creates a queue with maximum [capacity] elements. -+ This is burst capacity, each element is further classified according to [classify], -+ and each class can have its own [limit]. -+ [capacity] is enforced as an overall limit. -+ The [limit] can be dynamic, and can be smaller than the number of elements already queued of that class, -+ in which case those elements are considered to use "burst capacity". -+ *) -+ val create: capacity:int -> classify:('a -> 'b) -> limit:('b -> int) -> ('a, 'b) t -+ -+ (** [clear q] discards all elements from [q] *) -+ val clear: ('a, 'b) t -> unit -+ -+ (** [can_push q] when [length q < capacity]. *) -+ val can_push: ('a, 'b) t -> 'b -> bool -+ -+ (** [push e q] adds [e] at the end of queue [q] if [can_push q], or returns [None]. *) -+ val push: 'a -> ('a, 'b) t -> unit option -+ -+ (** [pop q] removes and returns first element in [q], or raises [Queue.Empty]. *) -+ val pop: ('a, 'b) t -> 'a -+ -+ (** [peek q] returns the first element in [q], or raises [Queue.Empty]. *) -+ val peek : ('a, 'b) t -> 'a -+ -+ (** [length q] returns the current number of elements in [q] *) -+ val length: ('a, 'b) t -> int -+ -+ (** [debug string_of_class q] prints queue usage statistics in an unspecified internal format. *) -+ val debug: ('b -> string) -> (_, 'b) t -> string -+end = struct -+ type ('a, 'b) t = -+ { q: 'a Queue.t -+ ; capacity: int -+ ; classify: 'a -> 'b -+ ; limit: 'b -> int -+ ; class_count: ('b, int) Hashtbl.t -+ } -+ -+ let create ~capacity ~classify ~limit = -+ { capacity; q = Queue.create (); classify; limit; class_count = Hashtbl.create 3 } -+ -+ let get_count t classification = try Hashtbl.find t.class_count classification with Not_found -> 0 -+ -+ let can_push_internal t classification class_count = -+ Queue.length t.q < t.capacity && class_count < t.limit classification -+ -+ let ok = Some () -+ -+ let push e t = -+ let classification = t.classify e in -+ let class_count = get_count t classification in -+ if can_push_internal t classification class_count then begin -+ Queue.push e t.q; -+ Hashtbl.replace t.class_count classification (class_count + 1); -+ ok -+ end -+ else -+ None -+ -+ let can_push t classification = -+ can_push_internal t classification @@ get_count t classification -+ -+ let clear t = -+ Queue.clear t.q; -+ Hashtbl.reset t.class_count -+ -+ let pop t = -+ let e = Queue.pop t.q in -+ let classification = t.classify e in -+ let () = match get_count t classification - 1 with -+ | 0 -> Hashtbl.remove t.class_count classification (* reduces memusage *) -+ | n -> Hashtbl.replace t.class_count classification n -+ in -+ e -+ -+ let peek t = Queue.peek t.q -+ let length t = Queue.length t.q -+ -+ let debug string_of_class t = -+ let b = Buffer.create 128 in -+ Printf.bprintf b "BoundedQueue capacity: %d, used: {" t.capacity; -+ Hashtbl.iter (fun packet_class count -> -+ Printf.bprintf b " %s: %d" (string_of_class packet_class) count -+ ) t.class_count; -+ Printf.bprintf b "}"; -+ Buffer.contents b -+end -+ -+ - exception End_of_file - exception Eagain - exception Noent diff --git a/xsa326-4.16-oxenstored-07.patch b/xsa326-4.16-oxenstored-07.patch deleted file mode 100644 index ef02467..0000000 --- a/xsa326-4.16-oxenstored-07.patch +++ /dev/null @@ -1,872 +0,0 @@ -From bc0f05e6f3a3c93c853ceffd1f6d2022dc30fb77 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Date: Wed, 12 Oct 2022 19:13:04 +0100 -Subject: tools/ocaml: Limit maximum in-flight requests / outstanding replies -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Introduce a limit on the number of outstanding reply packets in the xenbus -queue. This limits the number of in-flight requests: when the output queue is -full we'll stop processing inputs until the output queue has room again. - -To avoid a busy loop on the Unix socket we only add it to the watched input -file descriptor set if we'd be able to call `input` on it. Even though Dom0 -is trusted and exempt from quotas a flood of events might cause a backlog -where events are produced faster than daemons in Dom0 can consume them, which -could lead to an unbounded queue size and OOM. - -Therefore the xenbus queue limit must apply to all connections, Dom0 is not -exempt from it, although if everything works correctly it will eventually -catch up. - -This prevents a malicious guest from sending more commands while it has -outstanding watch events or command replies in its input ring. However if it -can cause the generation of watch events by other means (e.g. by Dom0, or -another cooperative guest) and stop reading its own ring then watch events -would've queued up without limit. - -The xenstore protocol doesn't have a back-pressure mechanism, and doesn't -allow dropping watch events. In fact, dropping watch events is known to break -some pieces of normal functionality. This leaves little choice to safely -implement the xenstore protocol without exposing the xenstore daemon to -out-of-memory attacks. - -Implement the fix as pipes with bounded buffers: -* Use a bounded buffer for watch events -* The watch structure will have a bounded receiving pipe of watch events -* The source will have an "overflow" pipe of pending watch events it couldn't - deliver - -Items are queued up on one end and are sent as far along the pipe as possible: - - source domain -> watch -> xenbus of target -> xenstore ring/socket of target - -If the pipe is "full" at any point then back-pressure is applied and we prevent -more items from being queued up. For the source domain this means that we'll -stop accepting new commands as long as its pipe buffer is not empty. - -Before we try to enqueue an item we first check whether it is possible to send -it further down the pipe, by attempting to recursively flush the pipes. This -ensures that we retain the order of events as much as possible. - -We might break causality of watch events if the target domain's queue is full -and we need to start using the watch's queue. This is a breaking change in -the xenstore protocol, but only for domains which are not processing their -incoming ring as expected. - -When a watch is deleted its entire pending queue is dropped (no code is needed -for that, because it is part of the 'watch' type). - -There is a cache of watches that have pending events that we attempt to flush -at every cycle if possible. - -Introduce 3 limits here: -* quota-maxwatchevents on watch event destination: when this is hit the - source will not be allowed to queue up more watch events. -* quota-maxoustanding which is the number of responses not read from the ring: - once exceeded, no more inputs are processed until all outstanding replies - are consumed by the client. -* overflow queue on the watch event source: all watches that cannot be stored - on destination are queued up here, a single command can trigger multiple - watches (e.g. due to recursion). - -The overflow queue currently doesn't have an upper bound, it is difficult to -accurately calculate one as it depends on whether you are Dom0 and how many -watches each path has registered and how many watch events you can trigger -with a single command (e.g. a commit). However these events were already -using memory, this just moves them elsewhere, and as long as we correctly -block a domain it shouldn't result in unbounded memory usage. - -Note that Dom0 is not excluded from these checks, it is important that Dom0 is -especially not excluded when it is the source, since there are many ways in -which a guest could trigger Dom0 to send it watch events. - -This should protect against malicious frontends as long as the backend follows -the PV xenstore protocol and only exposes paths needed by the frontend, and -changes those paths at most once as a reaction to guest events, or protocol -state. - -The queue limits are per watch, and per domain-pair, so even if one -communication channel would be "blocked", others would keep working, and the -domain itself won't get blocked as long as it doesn't overflow the queue of -watch events. - -Similarly a malicious backend could cause the frontend to get blocked, but -this watch queue protects the frontend as well as long as it follows the PV -protocol. (Although note that protection against malicious backends is only a -best effort at the moment) - -This is part of XSA-326 / CVE-2022-42318. - -Reported-by: Julien Grall -Signed-off-by: Edwin Török -Acked-by: Christian Lindig - -diff --git a/tools/ocaml/libs/xb/xb.ml b/tools/ocaml/libs/xb/xb.ml -index 4197a3888a68..b292ed7a874d 100644 ---- a/tools/ocaml/libs/xb/xb.ml -+++ b/tools/ocaml/libs/xb/xb.ml -@@ -134,14 +134,44 @@ type backend = Fd of backend_fd | Xenmmap of backend_mmap - - type partial_buf = HaveHdr of Partial.pkt | NoHdr of int * bytes - -+(* -+ separate capacity reservation for replies and watch events: -+ this allows a domain to keep working even when under a constant flood of -+ watch events -+*) -+type capacity = { maxoutstanding: int; maxwatchevents: int } -+ -+module Queue = BoundedQueue -+ -+type packet_class = -+ | CommandReply -+ | Watchevent -+ -+let string_of_packet_class = function -+ | CommandReply -> "command_reply" -+ | Watchevent -> "watch_event" -+ - type t = - { - backend: backend; -- pkt_out: Packet.t Queue.t; -+ pkt_out: (Packet.t, packet_class) Queue.t; - mutable partial_in: partial_buf; - mutable partial_out: string; -+ capacity: capacity - } - -+let to_read con = -+ match con.partial_in with -+ | HaveHdr partial_pkt -> Partial.to_complete partial_pkt -+ | NoHdr (i, _) -> i -+ -+let debug t = -+ Printf.sprintf "XenBus state: partial_in: %d needed, partial_out: %d bytes, pkt_out: %d packets, %s" -+ (to_read t) -+ (String.length t.partial_out) -+ (Queue.length t.pkt_out) -+ (BoundedQueue.debug string_of_packet_class t.pkt_out) -+ - let init_partial_in () = NoHdr - (Partial.header_size (), Bytes.make (Partial.header_size()) '\000') - -@@ -199,7 +229,8 @@ let output con = - let s = if String.length con.partial_out > 0 then - con.partial_out - else if Queue.length con.pkt_out > 0 then -- Packet.to_string (Queue.pop con.pkt_out) -+ let pkt = Queue.pop con.pkt_out in -+ Packet.to_string pkt - else - "" in - (* send data from s, and save the unsent data to partial_out *) -@@ -212,12 +243,15 @@ let output con = - (* after sending one packet, partial is empty *) - con.partial_out = "" - -+(* we can only process an input packet if we're guaranteed to have room -+ to store the response packet *) -+let can_input con = Queue.can_push con.pkt_out CommandReply -+ - (* NB: can throw Reconnect *) - let input con = -- let to_read = -- match con.partial_in with -- | HaveHdr partial_pkt -> Partial.to_complete partial_pkt -- | NoHdr (i, _) -> i in -+ if not (can_input con) then None -+ else -+ let to_read = to_read con in - - (* try to get more data from input stream *) - let b = Bytes.make to_read '\000' in -@@ -243,11 +277,22 @@ let input con = - None - ) - --let newcon backend = { -+let classify t = -+ match t.Packet.ty with -+ | Op.Watchevent -> Watchevent -+ | _ -> CommandReply -+ -+let newcon ~capacity backend = -+ let limit = function -+ | CommandReply -> capacity.maxoutstanding -+ | Watchevent -> capacity.maxwatchevents -+ in -+ { - backend = backend; -- pkt_out = Queue.create (); -+ pkt_out = Queue.create ~capacity:(capacity.maxoutstanding + capacity.maxwatchevents) ~classify ~limit; - partial_in = init_partial_in (); - partial_out = ""; -+ capacity = capacity; - } - - let open_fd fd = newcon (Fd { fd = fd; }) -diff --git a/tools/ocaml/libs/xb/xb.mli b/tools/ocaml/libs/xb/xb.mli -index 91c682162cea..71b2754ca788 100644 ---- a/tools/ocaml/libs/xb/xb.mli -+++ b/tools/ocaml/libs/xb/xb.mli -@@ -66,10 +66,11 @@ type backend_mmap = { - type backend_fd = { fd : Unix.file_descr; } - type backend = Fd of backend_fd | Xenmmap of backend_mmap - type partial_buf = HaveHdr of Partial.pkt | NoHdr of int * bytes -+type capacity = { maxoutstanding: int; maxwatchevents: int } - type t - val init_partial_in : unit -> partial_buf - val reconnect : t -> unit --val queue : t -> Packet.t -> unit -+val queue : t -> Packet.t -> unit option - val read_fd : backend_fd -> 'a -> bytes -> int -> int - val read_mmap : backend_mmap -> 'a -> bytes -> int -> int - val read : t -> bytes -> int -> int -@@ -78,13 +79,14 @@ val write_mmap : backend_mmap -> 'a -> string -> int -> int - val write : t -> string -> int -> int - val output : t -> bool - val input : t -> Packet.t option --val newcon : backend -> t --val open_fd : Unix.file_descr -> t --val open_mmap : Xenmmap.mmap_interface -> (unit -> unit) -> t -+val newcon : capacity:capacity -> backend -> t -+val open_fd : Unix.file_descr -> capacity:capacity -> t -+val open_mmap : Xenmmap.mmap_interface -> (unit -> unit) -> capacity:capacity -> t - val close : t -> unit - val is_fd : t -> bool - val is_mmap : t -> bool - val output_len : t -> int -+val can_input: t -> bool - val has_new_output : t -> bool - val has_old_output : t -> bool - val has_output : t -> bool -@@ -93,3 +95,4 @@ val has_partial_input : t -> bool - val has_more_input : t -> bool - val is_selectable : t -> bool - val get_fd : t -> Unix.file_descr -+val debug: t -> string -diff --git a/tools/ocaml/libs/xs/queueop.ml b/tools/ocaml/libs/xs/queueop.ml -index 9ff5bbd529ce..4e532cdaeacb 100644 ---- a/tools/ocaml/libs/xs/queueop.ml -+++ b/tools/ocaml/libs/xs/queueop.ml -@@ -16,9 +16,10 @@ - open Xenbus - - let data_concat ls = (String.concat "\000" ls) ^ "\000" -+let queue con pkt = let r = Xb.queue con pkt in assert (r <> None) - let queue_path ty (tid: int) (path: string) con = - let data = data_concat [ path; ] in -- Xb.queue con (Xb.Packet.create tid 0 ty data) -+ queue con (Xb.Packet.create tid 0 ty data) - - (* operations *) - let directory tid path con = queue_path Xb.Op.Directory tid path con -@@ -27,48 +28,48 @@ let read tid path con = queue_path Xb.Op.Read tid path con - let getperms tid path con = queue_path Xb.Op.Getperms tid path con - - let debug commands con = -- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Debug (data_concat commands)) -+ queue con (Xb.Packet.create 0 0 Xb.Op.Debug (data_concat commands)) - - let watch path data con = - let data = data_concat [ path; data; ] in -- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Watch data) -+ queue con (Xb.Packet.create 0 0 Xb.Op.Watch data) - - let unwatch path data con = - let data = data_concat [ path; data; ] in -- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Unwatch data) -+ queue con (Xb.Packet.create 0 0 Xb.Op.Unwatch data) - - let transaction_start con = -- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Transaction_start (data_concat [])) -+ queue con (Xb.Packet.create 0 0 Xb.Op.Transaction_start (data_concat [])) - - let transaction_end tid commit con = - let data = data_concat [ (if commit then "T" else "F"); ] in -- Xb.queue con (Xb.Packet.create tid 0 Xb.Op.Transaction_end data) -+ queue con (Xb.Packet.create tid 0 Xb.Op.Transaction_end data) - - let introduce domid mfn port con = - let data = data_concat [ Printf.sprintf "%u" domid; - Printf.sprintf "%nu" mfn; - string_of_int port; ] in -- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Introduce data) -+ queue con (Xb.Packet.create 0 0 Xb.Op.Introduce data) - - let release domid con = - let data = data_concat [ Printf.sprintf "%u" domid; ] in -- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Release data) -+ queue con (Xb.Packet.create 0 0 Xb.Op.Release data) - - let resume domid con = - let data = data_concat [ Printf.sprintf "%u" domid; ] in -- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Resume data) -+ queue con (Xb.Packet.create 0 0 Xb.Op.Resume data) - - let getdomainpath domid con = - let data = data_concat [ Printf.sprintf "%u" domid; ] in -- Xb.queue con (Xb.Packet.create 0 0 Xb.Op.Getdomainpath data) -+ queue con (Xb.Packet.create 0 0 Xb.Op.Getdomainpath data) - - let write tid path value con = - let data = path ^ "\000" ^ value (* no NULL at the end *) in -- Xb.queue con (Xb.Packet.create tid 0 Xb.Op.Write data) -+ queue con (Xb.Packet.create tid 0 Xb.Op.Write data) - - let mkdir tid path con = queue_path Xb.Op.Mkdir tid path con - let rm tid path con = queue_path Xb.Op.Rm tid path con - - let setperms tid path perms con = - let data = data_concat [ path; perms ] in -- Xb.queue con (Xb.Packet.create tid 0 Xb.Op.Setperms data) -+ queue con (Xb.Packet.create tid 0 Xb.Op.Setperms data) -diff --git a/tools/ocaml/libs/xs/xsraw.ml b/tools/ocaml/libs/xs/xsraw.ml -index 451f8b38dbcc..cbd17280600c 100644 ---- a/tools/ocaml/libs/xs/xsraw.ml -+++ b/tools/ocaml/libs/xs/xsraw.ml -@@ -36,8 +36,10 @@ type con = { - let close con = - Xb.close con.xb - -+let capacity = { Xb.maxoutstanding = 1; maxwatchevents = 0; } -+ - let open_fd fd = { -- xb = Xb.open_fd fd; -+ xb = Xb.open_fd ~capacity fd; - watchevents = Queue.create (); - } - -diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/connection.ml -index cc20e047d2b9..9624a5f9da2c 100644 ---- a/tools/ocaml/xenstored/connection.ml -+++ b/tools/ocaml/xenstored/connection.ml -@@ -20,12 +20,84 @@ open Stdext - - let xenstore_payload_max = 4096 (* xen/include/public/io/xs_wire.h *) - -+type 'a bounded_sender = 'a -> unit option -+(** a bounded sender accepts an ['a] item and returns: -+ None - if there is no room to accept the item -+ Some () - if it has successfully accepted/sent the item -+ *) -+ -+module BoundedPipe : sig -+ type 'a t -+ -+ (** [create ~capacity ~destination] creates a bounded pipe with a -+ local buffer holding at most [capacity] items. Once the buffer is -+ full it will not accept further items. items from the pipe are -+ flushed into [destination] as long as it accepts items. The -+ destination could be another pipe. -+ *) -+ val create: capacity:int -> destination:'a bounded_sender -> 'a t -+ -+ (** [is_empty t] returns whether the local buffer of [t] is empty. *) -+ val is_empty : _ t -> bool -+ -+ (** [length t] the number of items in the internal buffer *) -+ val length: _ t -> int -+ -+ (** [flush_pipe t] sends as many items from the local buffer as possible, -+ which could be none. *) -+ val flush_pipe: _ t -> unit -+ -+ (** [push t item] tries to [flush_pipe] and then push [item] -+ into the pipe if its [capacity] allows. -+ Returns [None] if there is no more room -+ *) -+ val push : 'a t -> 'a bounded_sender -+end = struct -+ (* items are enqueued in [q], and then flushed to [connect_to] *) -+ type 'a t = -+ { q: 'a Queue.t -+ ; destination: 'a bounded_sender -+ ; capacity: int -+ } -+ -+ let create ~capacity ~destination = -+ { q = Queue.create (); capacity; destination } -+ -+ let rec flush_pipe t = -+ if not Queue.(is_empty t.q) then -+ let item = Queue.peek t.q in -+ match t.destination item with -+ | None -> () (* no room *) -+ | Some () -> -+ (* successfully sent item to next stage *) -+ let _ = Queue.pop t.q in -+ (* continue trying to send more items *) -+ flush_pipe t -+ -+ let push t item = -+ (* first try to flush as many items from this pipe as possible to make room, -+ it is important to do this first to preserve the order of the items -+ *) -+ flush_pipe t; -+ if Queue.length t.q < t.capacity then begin -+ (* enqueue, instead of sending directly. -+ this ensures that [out] sees the items in the same order as we receive them -+ *) -+ Queue.push item t.q; -+ Some (flush_pipe t) -+ end else None -+ -+ let is_empty t = Queue.is_empty t.q -+ let length t = Queue.length t.q -+end -+ - type watch = { - con: t; - token: string; - path: string; - base: string; - is_relative: bool; -+ pending_watchevents: Xenbus.Xb.Packet.t BoundedPipe.t; - } - - and t = { -@@ -38,8 +110,36 @@ and t = { - anonid: int; - mutable stat_nb_ops: int; - mutable perm: Perms.Connection.t; -+ pending_source_watchevents: (watch * Xenbus.Xb.Packet.t) BoundedPipe.t - } - -+module Watch = struct -+ module T = struct -+ type t = watch -+ -+ let compare w1 w2 = -+ (* cannot compare watches from different connections *) -+ assert (w1.con == w2.con); -+ match String.compare w1.token w2.token with -+ | 0 -> String.compare w1.path w2.path -+ | n -> n -+ end -+ module Set = Set.Make(T) -+ -+ let flush_events t = -+ BoundedPipe.flush_pipe t.pending_watchevents; -+ not (BoundedPipe.is_empty t.pending_watchevents) -+ -+ let pending_watchevents t = -+ BoundedPipe.length t.pending_watchevents -+end -+ -+let source_flush_watchevents t = -+ BoundedPipe.flush_pipe t.pending_source_watchevents -+ -+let source_pending_watchevents t = -+ BoundedPipe.length t.pending_source_watchevents -+ - let mark_as_bad con = - match con.dom with - |None -> () -@@ -67,7 +167,8 @@ let watch_create ~con ~path ~token = { - token = token; - path = path; - base = get_path con; -- is_relative = path.[0] <> '/' && path.[0] <> '@' -+ is_relative = path.[0] <> '/' && path.[0] <> '@'; -+ pending_watchevents = BoundedPipe.create ~capacity:!Define.maxwatchevents ~destination:(Xenbus.Xb.queue con.xb) - } - - let get_con w = w.con -@@ -93,6 +194,9 @@ let make_perm dom = - Perms.Connection.create ~perms:[Perms.READ; Perms.WRITE] domid - - let create xbcon dom = -+ let destination (watch, pkt) = -+ BoundedPipe.push watch.pending_watchevents pkt -+ in - let id = - match dom with - | None -> let old = !anon_id_next in incr anon_id_next; old -@@ -109,6 +213,16 @@ let create xbcon dom = - anonid = id; - stat_nb_ops = 0; - perm = make_perm dom; -+ -+ (* the actual capacity will be lower, this is used as an overflow -+ buffer: anything that doesn't fit elsewhere gets put here, only -+ limited by the amount of watches that you can generate with a -+ single xenstore command (which is finite, although possibly very -+ large in theory for Dom0). Once the pipe here has any contents the -+ domain is blocked from sending more commands until it is empty -+ again though. -+ *) -+ pending_source_watchevents = BoundedPipe.create ~capacity:Sys.max_array_length ~destination - } - in - Logging.new_connection ~tid:Transaction.none ~con:(get_domstr con); -@@ -127,11 +241,17 @@ let set_target con target_domid = - - let is_backend_mmap con = Xenbus.Xb.is_mmap con.xb - --let send_reply con tid rid ty data = -+let packet_of con tid rid ty data = - if (String.length data) > xenstore_payload_max && (is_backend_mmap con) then -- Xenbus.Xb.queue con.xb (Xenbus.Xb.Packet.create tid rid Xenbus.Xb.Op.Error "E2BIG\000") -+ Xenbus.Xb.Packet.create tid rid Xenbus.Xb.Op.Error "E2BIG\000" - else -- Xenbus.Xb.queue con.xb (Xenbus.Xb.Packet.create tid rid ty data) -+ Xenbus.Xb.Packet.create tid rid ty data -+ -+let send_reply con tid rid ty data = -+ let result = Xenbus.Xb.queue con.xb (packet_of con tid rid ty data) in -+ (* should never happen: we only process an input packet when there is room for an output packet *) -+ (* and the limit for replies is different from the limit for watch events *) -+ assert (result <> None) - - let send_error con tid rid err = send_reply con tid rid Xenbus.Xb.Op.Error (err ^ "\000") - let send_ack con tid rid ty = send_reply con tid rid ty "OK\000" -@@ -181,11 +301,11 @@ let del_watch con path token = - apath, w - - let del_watches con = -- Hashtbl.clear con.watches; -+ Hashtbl.reset con.watches; - con.nb_watches <- 0 - - let del_transactions con = -- Hashtbl.clear con.transactions -+ Hashtbl.reset con.transactions - - let list_watches con = - let ll = Hashtbl.fold -@@ -208,21 +328,29 @@ let lookup_watch_perm path = function - let lookup_watch_perms oldroot root path = - lookup_watch_perm path oldroot @ lookup_watch_perm path (Some root) - --let fire_single_watch_unchecked watch = -+let fire_single_watch_unchecked source watch = - let data = Utils.join_by_null [watch.path; watch.token; ""] in -- send_reply watch.con Transaction.none 0 Xenbus.Xb.Op.Watchevent data -+ let pkt = packet_of watch.con Transaction.none 0 Xenbus.Xb.Op.Watchevent data in -+ -+ match BoundedPipe.push source.pending_source_watchevents (watch, pkt) with -+ | Some () -> () (* packet queued *) -+ | None -> -+ (* a well behaved Dom0 shouldn't be able to trigger this, -+ if it happens it is likely a Dom0 bug causing runaway memory usage -+ *) -+ failwith "watch event overflow, cannot happen" - --let fire_single_watch (oldroot, root) watch = -+let fire_single_watch source (oldroot, root) watch = - let abspath = get_watch_path watch.con watch.path |> Store.Path.of_string in - let perms = lookup_watch_perms oldroot root abspath in - if Perms.can_fire_watch watch.con.perm perms then -- fire_single_watch_unchecked watch -+ fire_single_watch_unchecked source watch - else - let perms = perms |> List.map (Perms.Node.to_string ~sep:" ") |> String.concat ", " in - let con = get_domstr watch.con in - Logging.watch_not_fired ~con perms (Store.Path.to_string abspath) - --let fire_watch roots watch path = -+let fire_watch source roots watch path = - let new_path = - if watch.is_relative && path.[0] = '/' - then begin -@@ -232,7 +360,7 @@ let fire_watch roots watch path = - end else - path - in -- fire_single_watch roots { watch with path = new_path } -+ fire_single_watch source roots { watch with path = new_path } - - (* Search for a valid unused transaction id. *) - let rec valid_transaction_id con proposed_id = -@@ -280,6 +408,7 @@ let do_input con = Xenbus.Xb.input con.xb - let has_partial_input con = Xenbus.Xb.has_partial_input con.xb - let has_more_input con = Xenbus.Xb.has_more_input con.xb - -+let can_input con = Xenbus.Xb.can_input con.xb && BoundedPipe.is_empty con.pending_source_watchevents - let has_output con = Xenbus.Xb.has_output con.xb - let has_old_output con = Xenbus.Xb.has_old_output con.xb - let has_new_output con = Xenbus.Xb.has_new_output con.xb -@@ -323,7 +452,7 @@ let prevents_live_update con = not (is_bad con) - && (has_extra_connection_data con || has_transaction_data con) - - let has_more_work con = -- has_more_input con || not (has_old_output con) && has_new_output con -+ (has_more_input con && can_input con) || not (has_old_output con) && has_new_output con - - let incr_ops con = con.stat_nb_ops <- con.stat_nb_ops + 1 - -diff --git a/tools/ocaml/xenstored/connections.ml b/tools/ocaml/xenstored/connections.ml -index 3c7429fe7f61..7d68c583b43a 100644 ---- a/tools/ocaml/xenstored/connections.ml -+++ b/tools/ocaml/xenstored/connections.ml -@@ -22,22 +22,30 @@ type t = { - domains: (int, Connection.t) Hashtbl.t; - ports: (Xeneventchn.t, Connection.t) Hashtbl.t; - mutable watches: Connection.watch list Trie.t; -+ mutable has_pending_watchevents: Connection.Watch.Set.t - } - - let create () = { - anonymous = Hashtbl.create 37; - domains = Hashtbl.create 37; - ports = Hashtbl.create 37; -- watches = Trie.create () -+ watches = Trie.create (); -+ has_pending_watchevents = Connection.Watch.Set.empty; - } - -+let get_capacity () = -+ (* not multiplied by maxwatch on purpose: 2nd queue in watch itself! *) -+ { Xenbus.Xb.maxoutstanding = !Define.maxoutstanding; maxwatchevents = !Define.maxwatchevents } -+ - let add_anonymous cons fd = -- let xbcon = Xenbus.Xb.open_fd fd in -+ let capacity = get_capacity () in -+ let xbcon = Xenbus.Xb.open_fd fd ~capacity in - let con = Connection.create xbcon None in - Hashtbl.add cons.anonymous (Xenbus.Xb.get_fd xbcon) con - - let add_domain cons dom = -- let xbcon = Xenbus.Xb.open_mmap (Domain.get_interface dom) (fun () -> Domain.notify dom) in -+ let capacity = get_capacity () in -+ let xbcon = Xenbus.Xb.open_mmap ~capacity (Domain.get_interface dom) (fun () -> Domain.notify dom) in - let con = Connection.create xbcon (Some dom) in - Hashtbl.add cons.domains (Domain.get_id dom) con; - match Domain.get_port dom with -@@ -48,7 +56,9 @@ let select ?(only_if = (fun _ -> true)) cons = - Hashtbl.fold (fun _ con (ins, outs) -> - if (only_if con) then ( - let fd = Connection.get_fd con in -- (fd :: ins, if Connection.has_output con then fd :: outs else outs) -+ let in_fds = if Connection.can_input con then fd :: ins else ins in -+ let out_fds = if Connection.has_output con then fd :: outs else outs in -+ in_fds, out_fds - ) else (ins, outs) - ) - cons.anonymous ([], []) -@@ -67,10 +77,17 @@ let del_watches_of_con con watches = - | [] -> None - | ws -> Some ws - -+let del_watches cons con = -+ Connection.del_watches con; -+ cons.watches <- Trie.map (del_watches_of_con con) cons.watches; -+ cons.has_pending_watchevents <- -+ cons.has_pending_watchevents |> Connection.Watch.Set.filter @@ fun w -> -+ Connection.get_con w != con -+ - let del_anonymous cons con = - try - Hashtbl.remove cons.anonymous (Connection.get_fd con); -- cons.watches <- Trie.map (del_watches_of_con con) cons.watches; -+ del_watches cons con; - Connection.close con - with exn -> - debug "del anonymous %s" (Printexc.to_string exn) -@@ -85,7 +102,7 @@ let del_domain cons id = - | Some p -> Hashtbl.remove cons.ports p - | None -> ()) - | None -> ()); -- cons.watches <- Trie.map (del_watches_of_con con) cons.watches; -+ del_watches cons con; - Connection.close con - with exn -> - debug "del domain %u: %s" id (Printexc.to_string exn) -@@ -136,31 +153,33 @@ let del_watch cons con path token = - cons.watches <- Trie.set cons.watches key watches; - watch - --let del_watches cons con = -- Connection.del_watches con; -- cons.watches <- Trie.map (del_watches_of_con con) cons.watches -- - (* path is absolute *) --let fire_watches ?oldroot root cons path recurse = -+let fire_watches ?oldroot source root cons path recurse = - let key = key_of_path path in - let path = Store.Path.to_string path in - let roots = oldroot, root in - let fire_watch _ = function - | None -> () -- | Some watches -> List.iter (fun w -> Connection.fire_watch roots w path) watches -+ | Some watches -> List.iter (fun w -> Connection.fire_watch source roots w path) watches - in - let fire_rec _x = function - | None -> () - | Some watches -> -- List.iter (Connection.fire_single_watch roots) watches -+ List.iter (Connection.fire_single_watch source roots) watches - in - Trie.iter_path fire_watch cons.watches key; - if recurse then - Trie.iter fire_rec (Trie.sub cons.watches key) - -+let send_watchevents cons con = -+ cons.has_pending_watchevents <- -+ cons.has_pending_watchevents |> Connection.Watch.Set.filter Connection.Watch.flush_events; -+ Connection.source_flush_watchevents con -+ - let fire_spec_watches root cons specpath = -+ let source = find_domain cons 0 in - iter cons (fun con -> -- List.iter (Connection.fire_single_watch (None, root)) (Connection.get_watches con specpath)) -+ List.iter (Connection.fire_single_watch source (None, root)) (Connection.get_watches con specpath)) - - let set_target cons domain target_domain = - let con = find_domain cons domain in -@@ -197,6 +216,16 @@ let debug cons = - let domains = Hashtbl.fold (fun _ con accu -> Connection.debug con :: accu) cons.domains [] in - String.concat "" (domains @ anonymous) - -+let debug_watchevents cons con = -+ (* == (physical equality) -+ has to be used here because w.con.xb.backend might contain a [unit->unit] value causing regular -+ comparison to fail due to having a 'functional value' which cannot be compared. -+ *) -+ let s = cons.has_pending_watchevents |> Connection.Watch.Set.filter (fun w -> w.con == con) in -+ let pending = s |> Connection.Watch.Set.elements -+ |> List.map (fun w -> Connection.Watch.pending_watchevents w) |> List.fold_left (+) 0 in -+ Printf.sprintf "Watches with pending events: %d, pending events total: %d" (Connection.Watch.Set.cardinal s) pending -+ - let filter ~f cons = - let fold _ v acc = if f v then v :: acc else acc in - [] -diff --git a/tools/ocaml/xenstored/define.ml b/tools/ocaml/xenstored/define.ml -index ba63a8147e09..327b6d795ec7 100644 ---- a/tools/ocaml/xenstored/define.ml -+++ b/tools/ocaml/xenstored/define.ml -@@ -24,6 +24,13 @@ let default_config_dir = Paths.xen_config_dir - let maxwatch = ref (100) - let maxtransaction = ref (10) - let maxrequests = ref (1024) (* maximum requests per transaction *) -+let maxoutstanding = ref (1024) (* maximum outstanding requests, i.e. in-flight requests / domain *) -+let maxwatchevents = ref (1024) -+(* -+ maximum outstanding watch events per watch, -+ recommended >= maxoutstanding to avoid blocking backend transactions due to -+ malicious frontends -+ *) - - let gc_max_overhead = ref 120 (* 120% see comment in xenstored.ml *) - let conflict_burst_limit = ref 5.0 -diff --git a/tools/ocaml/xenstored/oxenstored.conf.in b/tools/ocaml/xenstored/oxenstored.conf.in -index 4ae48e42d47d..9d034e744b4b 100644 ---- a/tools/ocaml/xenstored/oxenstored.conf.in -+++ b/tools/ocaml/xenstored/oxenstored.conf.in -@@ -62,6 +62,8 @@ quota-maxwatch = 100 - quota-transaction = 10 - quota-maxrequests = 1024 - quota-path-max = 1024 -+quota-maxoutstanding = 1024 -+quota-maxwatchevents = 1024 - - # Activate filed base backend - persistent = false -diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml -index 2d67456a2aa0..6dcedfda86e4 100644 ---- a/tools/ocaml/xenstored/process.ml -+++ b/tools/ocaml/xenstored/process.ml -@@ -57,7 +57,7 @@ let split_one_path data con = - | path :: "" :: [] -> Store.Path.create path (Connection.get_path con) - | _ -> raise Invalid_Cmd_Args - --let process_watch t cons = -+let process_watch source t cons = - let oldroot = t.Transaction.oldroot in - let newroot = Store.get_root t.store in - let ops = Transaction.get_paths t |> List.rev in -@@ -67,8 +67,9 @@ let process_watch t cons = - | Xenbus.Xb.Op.Rm -> true, None, oldroot - | Xenbus.Xb.Op.Setperms -> false, Some oldroot, newroot - | _ -> raise (Failure "huh ?") in -- Connections.fire_watches ?oldroot root cons (snd op) recurse in -- List.iter (fun op -> do_op_watch op cons) ops -+ Connections.fire_watches ?oldroot source root cons (snd op) recurse in -+ List.iter (fun op -> do_op_watch op cons) ops; -+ Connections.send_watchevents cons source - - let create_implicit_path t perm path = - let dirname = Store.Path.get_parent path in -@@ -234,6 +235,20 @@ let do_debug con t _domains cons data = - | "watches" :: _ -> - let watches = Connections.debug cons in - Some (watches ^ "\000") -+ | "xenbus" :: domid :: _ -> -+ let domid = int_of_string domid in -+ let con = Connections.find_domain cons domid in -+ let s = Printf.sprintf "xenbus: %s; overflow queue length: %d, can_input: %b, has_more_input: %b, has_old_output: %b, has_new_output: %b, has_more_work: %b. pending: %s" -+ (Xenbus.Xb.debug con.xb) -+ (Connection.source_pending_watchevents con) -+ (Connection.can_input con) -+ (Connection.has_more_input con) -+ (Connection.has_old_output con) -+ (Connection.has_new_output con) -+ (Connection.has_more_work con) -+ (Connections.debug_watchevents cons con) -+ in -+ Some s - | "mfn" :: domid :: _ -> - let domid = int_of_string domid in - let con = Connections.find_domain cons domid in -@@ -342,7 +357,7 @@ let reply_ack fct con t doms cons data = - fct con t doms cons data; - Packet.Ack (fun () -> - if Transaction.get_id t = Transaction.none then -- process_watch t cons -+ process_watch con t cons - ) - - let reply_data fct con t doms cons data = -@@ -501,7 +516,7 @@ let do_watch con t _domains cons data = - Packet.Ack (fun () -> - (* xenstore.txt says this watch is fired immediately, - implying even if path doesn't exist or is unreadable *) -- Connection.fire_single_watch_unchecked watch) -+ Connection.fire_single_watch_unchecked con watch) - - let do_unwatch con _t _domains cons data = - let (node, token) = -@@ -532,7 +547,7 @@ let do_transaction_end con t domains cons data = - if not success then - raise Transaction_again; - if commit then begin -- process_watch t cons; -+ process_watch con t cons; - match t.Transaction.ty with - | Transaction.No -> - () (* no need to record anything *) -@@ -699,7 +714,8 @@ let process_packet ~store ~cons ~doms ~con ~req = - let do_input store cons doms con = - let newpacket = - try -- Connection.do_input con -+ if Connection.can_input con then Connection.do_input con -+ else None - with Xenbus.Xb.Reconnect -> - info "%s requests a reconnect" (Connection.get_domstr con); - History.reconnect con; -@@ -727,6 +743,7 @@ let do_input store cons doms con = - Connection.incr_ops con - - let do_output _store _cons _doms con = -+ Connection.source_flush_watchevents con; - if Connection.has_output con then ( - if Connection.has_new_output con then ( - let packet = Connection.peek_output con in -diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml -index 3b57ad016dfb..c799e20f1145 100644 ---- a/tools/ocaml/xenstored/xenstored.ml -+++ b/tools/ocaml/xenstored/xenstored.ml -@@ -103,6 +103,8 @@ let parse_config filename = - ("quota-maxentity", Config.Set_int Quota.maxent); - ("quota-maxsize", Config.Set_int Quota.maxsize); - ("quota-maxrequests", Config.Set_int Define.maxrequests); -+ ("quota-maxoutstanding", Config.Set_int Define.maxoutstanding); -+ ("quota-maxwatchevents", Config.Set_int Define.maxwatchevents); - ("quota-path-max", Config.Set_int Define.path_max); - ("gc-max-overhead", Config.Set_int Define.gc_max_overhead); - ("test-eagain", Config.Set_bool Transaction.test_eagain); diff --git a/xsa326-4.16-oxenstored-08.patch b/xsa326-4.16-oxenstored-08.patch deleted file mode 100644 index 8a47200..0000000 --- a/xsa326-4.16-oxenstored-08.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 09aa10649f75a262028e9a9b7d859ef7efb23d54 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 29 Sep 2022 13:07:35 +0200 -Subject: SUPPORT.md: clarify support of untrusted driver domains with - oxenstored - -Add a support statement for the scope of support regarding different -Xenstore variants. Especially oxenstored does not (yet) have security -support of untrusted driver domains, as those might drive oxenstored -out of memory by creating lots of watch events for the guests they are -servicing. - -Add a statement regarding Live Update support of oxenstored. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Acked-by: George Dunlap -Acked-by: Julien Grall -Reviewed-by: Christian Lindig - -diff --git a/SUPPORT.md b/SUPPORT.md -index 85726102eab8..7d0cb34c8f6f 100644 ---- a/SUPPORT.md -+++ b/SUPPORT.md -@@ -179,13 +179,18 @@ Support for running qemu-xen device model in a linux stubdomain. - - Status: Tech Preview - --## Liveupdate of C xenstored daemon -+## Xenstore - -- Status: Tech Preview -+### C xenstored daemon - --## Liveupdate of OCaml xenstored daemon -+ Status: Supported -+ Status, Liveupdate: Tech Preview - -- Status: Tech Preview -+### OCaml xenstored daemon -+ -+ Status: Supported -+ Status, untrusted driver domains: Supported, not security supported -+ Status, Liveupdate: Not functional - - ## Toolstack/3rd party - diff --git a/xsa326-4.16-xenstored-01.patch b/xsa326-4.16-xenstored-01.patch deleted file mode 100644 index b4a4b0e..0000000 --- a/xsa326-4.16-xenstored-01.patch +++ /dev/null @@ -1,205 +0,0 @@ -From 5192f13a41661b1c1b9e0889d57c0f5b41925c39 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:07 +0200 -Subject: tools/xenstore: split up send_reply() - -Today send_reply() is used for both, normal request replies and watch -events. - -Split it up into send_reply() and send_event(). This will be used to -add some event specific handling. - -add_event() can be merged into send_event(), removing the need for an -intermediate memory allocation. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index e9c9695fd16e..249ad5ec6fb1 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -767,49 +767,32 @@ static void send_error(struct connection *conn, int error) - void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - const void *data, unsigned int len) - { -- struct buffered_data *bdata; -+ struct buffered_data *bdata = conn->in; -+ -+ assert(type != XS_WATCH_EVENT); - - if ( len > XENSTORE_PAYLOAD_MAX ) { - send_error(conn, E2BIG); - return; - } - -- /* Replies reuse the request buffer, events need a new one. */ -- if (type != XS_WATCH_EVENT) { -- bdata = conn->in; -- /* Drop asynchronous responses, e.g. errors for watch events. */ -- if (!bdata) -- return; -- bdata->inhdr = true; -- bdata->used = 0; -- conn->in = NULL; -- } else { -- /* Message is a child of the connection for auto-cleanup. */ -- bdata = new_buffer(conn); -+ if (!bdata) -+ return; -+ bdata->inhdr = true; -+ bdata->used = 0; - -- /* -- * Allocation failure here is unfortunate: we have no way to -- * tell anybody about it. -- */ -- if (!bdata) -- return; -- } - if (len <= DEFAULT_BUFFER_SIZE) - bdata->buffer = bdata->default_buffer; -- else -+ else { - bdata->buffer = talloc_array(bdata, char, len); -- if (!bdata->buffer) { -- if (type == XS_WATCH_EVENT) { -- /* Same as above: no way to tell someone. */ -- talloc_free(bdata); -+ if (!bdata->buffer) { -+ send_error(conn, ENOMEM); - return; - } -- /* re-establish request buffer for sending ENOMEM. */ -- conn->in = bdata; -- send_error(conn, ENOMEM); -- return; - } - -+ conn->in = NULL; -+ - /* Update relevant header fields and fill in the message body. */ - bdata->hdr.msg.type = type; - bdata->hdr.msg.len = len; -@@ -817,8 +800,39 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - - /* Queue for later transmission. */ - list_add_tail(&bdata->list, &conn->out_list); -+} - -- return; -+/* -+ * Send a watch event. -+ * As this is not directly related to the current command, errors can't be -+ * reported. -+ */ -+void send_event(struct connection *conn, const char *path, const char *token) -+{ -+ struct buffered_data *bdata; -+ unsigned int len; -+ -+ len = strlen(path) + 1 + strlen(token) + 1; -+ /* Don't try to send over-long events. */ -+ if (len > XENSTORE_PAYLOAD_MAX) -+ return; -+ -+ bdata = new_buffer(conn); -+ if (!bdata) -+ return; -+ -+ bdata->buffer = talloc_array(bdata, char, len); -+ if (!bdata->buffer) { -+ talloc_free(bdata); -+ return; -+ } -+ strcpy(bdata->buffer, path); -+ strcpy(bdata->buffer + strlen(path) + 1, token); -+ bdata->hdr.msg.type = XS_WATCH_EVENT; -+ bdata->hdr.msg.len = len; -+ -+ /* Queue for later transmission. */ -+ list_add_tail(&bdata->list, &conn->out_list); - } - - /* Some routines (write, mkdir, etc) just need a non-error return */ -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index 0004fa848c83..9af9af4390bd 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -187,6 +187,7 @@ unsigned int get_string(const struct buffered_data *data, unsigned int offset); - - void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - const void *data, unsigned int len); -+void send_event(struct connection *conn, const char *path, const char *token); - - /* Some routines (write, mkdir, etc) just need a non-error return */ - void send_ack(struct connection *conn, enum xsd_sockmsg_type type); -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index aca0a71bada1..99a2c266b28a 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -86,35 +86,6 @@ static const char *get_watch_path(const struct watch *watch, const char *name) - } - - /* -- * Send a watch event. -- * Temporary memory allocations are done with ctx. -- */ --static void add_event(struct connection *conn, -- const void *ctx, -- struct watch *watch, -- const char *name) --{ -- /* Data to send (node\0token\0). */ -- unsigned int len; -- char *data; -- -- name = get_watch_path(watch, name); -- -- len = strlen(name) + 1 + strlen(watch->token) + 1; -- /* Don't try to send over-long events. */ -- if (len > XENSTORE_PAYLOAD_MAX) -- return; -- -- data = talloc_array(ctx, char, len); -- if (!data) -- return; -- strcpy(data, name); -- strcpy(data + strlen(name) + 1, watch->token); -- send_reply(conn, XS_WATCH_EVENT, data, len); -- talloc_free(data); --} -- --/* - * Check permissions of a specific watch to fire: - * Either the node itself or its parent have to be readable by the connection - * the watch has been setup for. In case a watch event is created due to -@@ -190,10 +161,14 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, - list_for_each_entry(watch, &i->watches, list) { - if (exact) { - if (streq(name, watch->node)) -- add_event(i, ctx, watch, name); -+ send_event(i, -+ get_watch_path(watch, name), -+ watch->token); - } else { - if (is_child(name, watch->node)) -- add_event(i, ctx, watch, name); -+ send_event(i, -+ get_watch_path(watch, name), -+ watch->token); - } - } - } -@@ -292,7 +267,7 @@ int do_watch(struct connection *conn, struct buffered_data *in) - send_ack(conn, XS_WATCH); - - /* We fire once up front: simplifies clients and restart. */ -- add_event(conn, in, watch, watch->node); -+ send_event(conn, get_watch_path(watch, watch->node), watch->token); - - return 0; - } diff --git a/xsa326-4.16-xenstored-02.patch b/xsa326-4.16-xenstored-02.patch deleted file mode 100644 index 540ab03..0000000 --- a/xsa326-4.16-xenstored-02.patch +++ /dev/null @@ -1,108 +0,0 @@ -From 0a4c86f8a8febd85610496470123adfc4fbc1c5d Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:07 +0200 -Subject: tools/xenstore: add helpers to free struct buffered_data - -Add two helpers for freeing struct buffered_data: free_buffered_data() -for freeing one instance and conn_free_buffered_data() for freeing all -instances for a connection. - -This is avoiding duplicated code and will help later when more actions -are needed when freeing a struct buffered_data. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 249ad5ec6fb1..527a1ebdeded 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -211,6 +211,21 @@ void reopen_log(void) - } - } - -+static void free_buffered_data(struct buffered_data *out, -+ struct connection *conn) -+{ -+ list_del(&out->list); -+ talloc_free(out); -+} -+ -+void conn_free_buffered_data(struct connection *conn) -+{ -+ struct buffered_data *out; -+ -+ while ((out = list_top(&conn->out_list, struct buffered_data, list))) -+ free_buffered_data(out, conn); -+} -+ - static bool write_messages(struct connection *conn) - { - int ret; -@@ -254,8 +269,7 @@ static bool write_messages(struct connection *conn) - - trace_io(conn, out, 1); - -- list_del(&out->list); -- talloc_free(out); -+ free_buffered_data(out, conn); - - return true; - } -@@ -1506,18 +1520,12 @@ static struct { - */ - void ignore_connection(struct connection *conn) - { -- struct buffered_data *out, *tmp; -- - trace("CONN %p ignored\n", conn); - - conn->is_ignored = true; - conn_delete_all_watches(conn); - conn_delete_all_transactions(conn); -- -- list_for_each_entry_safe(out, tmp, &conn->out_list, list) { -- list_del(&out->list); -- talloc_free(out); -- } -+ conn_free_buffered_data(conn); - - talloc_free(conn->in); - conn->in = NULL; -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index 9af9af4390bd..e7ee87825c3b 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -276,6 +276,8 @@ int remember_string(struct hashtable *hash, const char *str); - - void set_tdb_key(const char *name, TDB_DATA *key); - -+void conn_free_buffered_data(struct connection *conn); -+ - const char *dump_state_global(FILE *fp); - const char *dump_state_buffered_data(FILE *fp, const struct connection *c, - struct xs_state_connection *sc); -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index d03c7d93a9e7..93c4c1edcdd1 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -411,15 +411,10 @@ static struct domain *find_domain_by_domid(unsigned int domid) - static void domain_conn_reset(struct domain *domain) - { - struct connection *conn = domain->conn; -- struct buffered_data *out; - - conn_delete_all_watches(conn); - conn_delete_all_transactions(conn); -- -- while ((out = list_top(&conn->out_list, struct buffered_data, list))) { -- list_del(&out->list); -- talloc_free(out); -- } -+ conn_free_buffered_data(conn); - - talloc_free(conn->in); - diff --git a/xsa326-4.16-xenstored-03.patch b/xsa326-4.16-xenstored-03.patch deleted file mode 100644 index a3a0d81..0000000 --- a/xsa326-4.16-xenstored-03.patch +++ /dev/null @@ -1,192 +0,0 @@ -From a6c4198242bf69bea1825492b7665b559023390c Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:07 +0200 -Subject: tools/xenstore: reduce number of watch events - -When removing a watched node outside of a transaction, two watch events -are being produced instead of just a single one. - -When finalizing a transaction watch events can be generated for each -node which is being modified, even if outside a transaction such -modifications might not have resulted in a watch event. - -This happens e.g.: - -- for nodes which are only modified due to added/removed child entries -- for nodes being removed or created implicitly (e.g. creation of a/b/c - is implicitly creating a/b, resulting in watch events for a, a/b and - a/b/c instead of a/b/c only) - -Avoid these additional watch events, in order to reduce the needed -memory inside Xenstore for queueing them. - -This is being achieved by adding event flags to struct accessed_node -specifying whether an event should be triggered, and whether it should -be an exact match of the modified path. Both flags can be set from -fire_watches() instead of implying them only. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 527a1ebdeded..bf2243873901 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1295,7 +1295,7 @@ static void delete_child(struct connection *conn, - } - - static int delete_node(struct connection *conn, const void *ctx, -- struct node *parent, struct node *node) -+ struct node *parent, struct node *node, bool watch_exact) - { - char *name; - -@@ -1307,7 +1307,7 @@ static int delete_node(struct connection *conn, const void *ctx, - node->children); - child = name ? read_node(conn, node, name) : NULL; - if (child) { -- if (delete_node(conn, ctx, node, child)) -+ if (delete_node(conn, ctx, node, child, true)) - return errno; - } else { - trace("delete_node: Error deleting child '%s/%s'!\n", -@@ -1319,7 +1319,12 @@ static int delete_node(struct connection *conn, const void *ctx, - talloc_free(name); - } - -- fire_watches(conn, ctx, node->name, node, true, NULL); -+ /* -+ * Fire the watches now, when we can still see the node permissions. -+ * This fine as we are single threaded and the next possible read will -+ * be handled only after the node has been really removed. -+ */ -+ fire_watches(conn, ctx, node->name, node, watch_exact, NULL); - delete_node_single(conn, node); - delete_child(conn, parent, basename(node->name)); - talloc_free(node); -@@ -1345,13 +1350,7 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, - return (errno == ENOMEM) ? ENOMEM : EINVAL; - node->parent = parent; - -- /* -- * Fire the watches now, when we can still see the node permissions. -- * This fine as we are single threaded and the next possible read will -- * be handled only after the node has been really removed. -- */ -- fire_watches(conn, ctx, name, node, false, NULL); -- return delete_node(conn, ctx, parent, node); -+ return delete_node(conn, ctx, parent, node, false); - } - - -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index faf6c930e42a..54432907fc76 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -130,6 +130,10 @@ struct accessed_node - - /* Transaction node in data base? */ - bool ta_node; -+ -+ /* Watch event flags. */ -+ bool fire_watch; -+ bool watch_exact; - }; - - struct changed_domain -@@ -324,6 +328,29 @@ int access_node(struct connection *conn, struct node *node, - } - - /* -+ * A watch event should be fired for a node modified inside a transaction. -+ * Set the corresponding information. A non-exact event is replacing an exact -+ * one, but not the other way round. -+ */ -+void queue_watches(struct connection *conn, const char *name, bool watch_exact) -+{ -+ struct accessed_node *i; -+ -+ i = find_accessed_node(conn->transaction, name); -+ if (!i) { -+ conn->transaction->fail = true; -+ return; -+ } -+ -+ if (!i->fire_watch) { -+ i->fire_watch = true; -+ i->watch_exact = watch_exact; -+ } else if (!watch_exact) { -+ i->watch_exact = false; -+ } -+} -+ -+/* - * Finalize transaction: - * Walk through accessed nodes and check generation against global data. - * If all entries match, read the transaction entries and write them without -@@ -377,15 +404,15 @@ static int finalize_transaction(struct connection *conn, - ret = tdb_store(tdb_ctx, key, data, - TDB_REPLACE); - talloc_free(data.dptr); -- if (ret) -- goto err; -- fire_watches(conn, trans, i->node, NULL, false, -- i->perms.p ? &i->perms : NULL); - } else { -- fire_watches(conn, trans, i->node, NULL, false, -+ ret = tdb_delete(tdb_ctx, key); -+ } -+ if (ret) -+ goto err; -+ if (i->fire_watch) { -+ fire_watches(conn, trans, i->node, NULL, -+ i->watch_exact, - i->perms.p ? &i->perms : NULL); -- if (tdb_delete(tdb_ctx, key)) -- goto err; - } - } - -diff --git a/tools/xenstore/xenstored_transaction.h b/tools/xenstore/xenstored_transaction.h -index 14062730e3c9..0093cac807e3 100644 ---- a/tools/xenstore/xenstored_transaction.h -+++ b/tools/xenstore/xenstored_transaction.h -@@ -42,6 +42,9 @@ void transaction_entry_dec(struct transaction *trans, unsigned int domid); - int access_node(struct connection *conn, struct node *node, - enum node_access_type type, TDB_DATA *key); - -+/* Queue watches for a modified node. */ -+void queue_watches(struct connection *conn, const char *name, bool watch_exact); -+ - /* Prepend the transaction to name if appropriate. */ - int transaction_prepend(struct connection *conn, const char *name, - TDB_DATA *key); -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index 99a2c266b28a..205d9d8ea116 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -29,6 +29,7 @@ - #include "xenstore_lib.h" - #include "utils.h" - #include "xenstored_domain.h" -+#include "xenstored_transaction.h" - - extern int quota_nb_watch_per_domain; - -@@ -143,9 +144,11 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, - struct connection *i; - struct watch *watch; - -- /* During transactions, don't fire watches. */ -- if (conn && conn->transaction) -+ /* During transactions, don't fire watches, but queue them. */ -+ if (conn && conn->transaction) { -+ queue_watches(conn, name, exact); - return; -+ } - - /* Create an event for each watch. */ - list_for_each_entry(i, &connections, list) { diff --git a/xsa326-4.16-xenstored-04.patch b/xsa326-4.16-xenstored-04.patch deleted file mode 100644 index facbba4..0000000 --- a/xsa326-4.16-xenstored-04.patch +++ /dev/null @@ -1,302 +0,0 @@ -From 2feed737530592688382c655680982e10951c1ec Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:07 +0200 -Subject: tools/xenstore: let unread watch events time out - -A future modification will limit the number of outstanding requests -for a domain, where "outstanding" means that the response of the -request or any resulting watch event hasn't been consumed yet. - -In order to avoid a malicious guest being capable to block other guests -by not reading watch events, add a timeout for watch events. In case a -watch event hasn't been consumed after this timeout, it is being -deleted. Set the default timeout to 20 seconds (a random value being -not too high). - -In order to support to specify other timeout values in future, use a -generic command line option for that purpose: - ---timeout|-w watch-event= - -This is part of XSA-326 / CVE-2022-42311. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index bf2243873901..45244c021cd3 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -108,6 +108,8 @@ int quota_max_transaction = 10; - int quota_nb_perms_per_node = 5; - int quota_max_path_len = XENSTORE_REL_PATH_MAX; - -+unsigned int timeout_watch_event_msec = 20000; -+ - void trace(const char *fmt, ...) - { - va_list arglist; -@@ -211,19 +213,92 @@ void reopen_log(void) - } - } - -+static uint64_t get_now_msec(void) -+{ -+ struct timespec now_ts; -+ -+ if (clock_gettime(CLOCK_MONOTONIC, &now_ts)) -+ barf_perror("Could not find time (clock_gettime failed)"); -+ -+ return now_ts.tv_sec * 1000 + now_ts.tv_nsec / 1000000; -+} -+ - static void free_buffered_data(struct buffered_data *out, - struct connection *conn) - { -+ struct buffered_data *req; -+ - list_del(&out->list); -+ -+ /* -+ * Update conn->timeout_msec with the next found timeout value in the -+ * queued pending requests. -+ */ -+ if (out->timeout_msec) { -+ conn->timeout_msec = 0; -+ list_for_each_entry(req, &conn->out_list, list) { -+ if (req->timeout_msec) { -+ conn->timeout_msec = req->timeout_msec; -+ break; -+ } -+ } -+ } -+ - talloc_free(out); - } - -+static void check_event_timeout(struct connection *conn, uint64_t msecs, -+ int *ptimeout) -+{ -+ uint64_t delta; -+ struct buffered_data *out, *tmp; -+ -+ if (!conn->timeout_msec) -+ return; -+ -+ delta = conn->timeout_msec - msecs; -+ if (conn->timeout_msec <= msecs) { -+ delta = 0; -+ list_for_each_entry_safe(out, tmp, &conn->out_list, list) { -+ /* -+ * Only look at buffers with timeout and no data -+ * already written to the ring. -+ */ -+ if (out->timeout_msec && out->inhdr && !out->used) { -+ if (out->timeout_msec > msecs) { -+ conn->timeout_msec = out->timeout_msec; -+ delta = conn->timeout_msec - msecs; -+ break; -+ } -+ -+ /* -+ * Free out without updating conn->timeout_msec, -+ * as the update is done in this loop already. -+ */ -+ out->timeout_msec = 0; -+ trace("watch event path %s for domain %u timed out\n", -+ out->buffer, conn->id); -+ free_buffered_data(out, conn); -+ } -+ } -+ if (!delta) { -+ conn->timeout_msec = 0; -+ return; -+ } -+ } -+ -+ if (*ptimeout == -1 || *ptimeout > delta) -+ *ptimeout = delta; -+} -+ - void conn_free_buffered_data(struct connection *conn) - { - struct buffered_data *out; - - while ((out = list_top(&conn->out_list, struct buffered_data, list))) - free_buffered_data(out, conn); -+ -+ conn->timeout_msec = 0; - } - - static bool write_messages(struct connection *conn) -@@ -411,6 +486,7 @@ static void initialize_fds(int *p_sock_pollfd_idx, int *ptimeout) - { - struct connection *conn; - struct wrl_timestampt now; -+ uint64_t msecs; - - if (fds) - memset(fds, 0, sizeof(struct pollfd) * current_array_size); -@@ -431,10 +507,12 @@ static void initialize_fds(int *p_sock_pollfd_idx, int *ptimeout) - - wrl_gettime_now(&now); - wrl_log_periodic(now); -+ msecs = get_now_msec(); - - list_for_each_entry(conn, &connections, list) { - if (conn->domain) { - wrl_check_timeout(conn->domain, now, ptimeout); -+ check_event_timeout(conn, msecs, ptimeout); - if (conn_can_read(conn) || - (conn_can_write(conn) && - !list_empty(&conn->out_list))) -@@ -794,6 +872,7 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - return; - bdata->inhdr = true; - bdata->used = 0; -+ bdata->timeout_msec = 0; - - if (len <= DEFAULT_BUFFER_SIZE) - bdata->buffer = bdata->default_buffer; -@@ -845,6 +924,12 @@ void send_event(struct connection *conn, const char *path, const char *token) - bdata->hdr.msg.type = XS_WATCH_EVENT; - bdata->hdr.msg.len = len; - -+ if (timeout_watch_event_msec && domain_is_unprivileged(conn)) { -+ bdata->timeout_msec = get_now_msec() + timeout_watch_event_msec; -+ if (!conn->timeout_msec) -+ conn->timeout_msec = bdata->timeout_msec; -+ } -+ - /* Queue for later transmission. */ - list_add_tail(&bdata->list, &conn->out_list); - } -@@ -2201,6 +2286,9 @@ static void usage(void) - " -t, --transaction limit the number of transaction allowed per domain,\n" - " -A, --perm-nb limit the number of permissions per node,\n" - " -M, --path-max limit the allowed Xenstore node path length,\n" -+" -w, --timeout = set the timeout in seconds for ,\n" -+" allowed timeout candidates are:\n" -+" watch-event: time a watch-event is kept pending\n" - " -R, --no-recovery to request that no recovery should be attempted when\n" - " the store is corrupted (debug only),\n" - " -I, --internal-db store database in memory, not on disk\n" -@@ -2223,6 +2311,7 @@ static struct option options[] = { - { "transaction", 1, NULL, 't' }, - { "perm-nb", 1, NULL, 'A' }, - { "path-max", 1, NULL, 'M' }, -+ { "timeout", 1, NULL, 'w' }, - { "no-recovery", 0, NULL, 'R' }, - { "internal-db", 0, NULL, 'I' }, - { "verbose", 0, NULL, 'V' }, -@@ -2236,6 +2325,39 @@ int dom0_domid = 0; - int dom0_event = 0; - int priv_domid = 0; - -+static int get_optval_int(const char *arg) -+{ -+ char *end; -+ long val; -+ -+ val = strtol(arg, &end, 10); -+ if (!*arg || *end || val < 0 || val > INT_MAX) -+ barf("invalid parameter value \"%s\"\n", arg); -+ -+ return val; -+} -+ -+static bool what_matches(const char *arg, const char *what) -+{ -+ unsigned int what_len = strlen(what); -+ -+ return !strncmp(arg, what, what_len) && arg[what_len] == '='; -+} -+ -+static void set_timeout(const char *arg) -+{ -+ const char *eq = strchr(arg, '='); -+ int val; -+ -+ if (!eq) -+ barf("quotas must be specified via =\n"); -+ val = get_optval_int(eq + 1); -+ if (what_matches(arg, "watch-event")) -+ timeout_watch_event_msec = val * 1000; -+ else -+ barf("unknown timeout \"%s\"\n", arg); -+} -+ - int main(int argc, char *argv[]) - { - int opt; -@@ -2250,7 +2372,7 @@ int main(int argc, char *argv[]) - orig_argc = argc; - orig_argv = argv; - -- while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:T:RVW:U", options, -+ while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:T:RVW:w:U", options, - NULL)) != -1) { - switch (opt) { - case 'D': -@@ -2300,6 +2422,9 @@ int main(int argc, char *argv[]) - quota_max_path_len = min(XENSTORE_REL_PATH_MAX, - quota_max_path_len); - break; -+ case 'w': -+ set_timeout(optarg); -+ break; - case 'e': - dom0_event = strtol(optarg, NULL, 10); - break; -@@ -2741,6 +2866,12 @@ static void add_buffered_data(struct buffered_data *bdata, - barf("error restoring buffered data"); - - memcpy(bdata->buffer, data, len); -+ if (bdata->hdr.msg.type == XS_WATCH_EVENT && timeout_watch_event_msec && -+ domain_is_unprivileged(conn)) { -+ bdata->timeout_msec = get_now_msec() + timeout_watch_event_msec; -+ if (!conn->timeout_msec) -+ conn->timeout_msec = bdata->timeout_msec; -+ } - - /* Queue for later transmission. */ - list_add_tail(&bdata->list, &conn->out_list); -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index e7ee87825c3b..8a81fc693f01 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -27,6 +27,7 @@ - #include - #include - #include -+#include - #include - - #include "xenstore_lib.h" -@@ -67,6 +68,8 @@ struct buffered_data - char raw[sizeof(struct xsd_sockmsg)]; - } hdr; - -+ uint64_t timeout_msec; -+ - /* The actual data. */ - char *buffer; - char default_buffer[DEFAULT_BUFFER_SIZE]; -@@ -118,6 +121,7 @@ struct connection - - /* Buffered output data */ - struct list_head out_list; -+ uint64_t timeout_msec; - - /* Transaction context for current request (NULL if none). */ - struct transaction *transaction; -@@ -244,6 +248,8 @@ extern int dom0_event; - extern int priv_domid; - extern int quota_nb_entry_per_domain; - -+extern unsigned int timeout_watch_event_msec; -+ - /* Map the kernel's xenstore page. */ - void *xenbus_map(void); - void unmap_xenbus(void *interface); diff --git a/xsa326-4.16-xenstored-05.patch b/xsa326-4.16-xenstored-05.patch deleted file mode 100644 index 77f9c25..0000000 --- a/xsa326-4.16-xenstored-05.patch +++ /dev/null @@ -1,443 +0,0 @@ -From 2eee122a45eb4a218596b103ce7f0759a824cf2e Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:08 +0200 -Subject: tools/xenstore: limit outstanding requests - -Add another quota for limiting the number of outstanding requests of a -guest. As the way to specify quotas on the command line is becoming -rather nasty, switch to a new scheme using [--quota|-Q] = -allowing to add more quotas in future easily. - -Set the default value to 20 (basically a random value not seeming to -be too high or too low). - -A request is said to be outstanding if any message generated by this -request (the direct response plus potential watch events) is not yet -completely stored into a ring buffer. The initial watch event sent as -a result of registering a watch is an exception. - -Note that across a live update the relation to buffered watch events -for other domains is lost. - -Use talloc_zero() for allocating the domain structure in order to have -all per-domain quota zeroed initially. - -This is part of XSA-326 / CVE-2022-42312. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Acked-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 45244c021cd3..488d540f3a32 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -107,6 +107,7 @@ int quota_max_entry_size = 2048; /* 2K */ - int quota_max_transaction = 10; - int quota_nb_perms_per_node = 5; - int quota_max_path_len = XENSTORE_REL_PATH_MAX; -+int quota_req_outstanding = 20; - - unsigned int timeout_watch_event_msec = 20000; - -@@ -223,12 +224,24 @@ static uint64_t get_now_msec(void) - return now_ts.tv_sec * 1000 + now_ts.tv_nsec / 1000000; - } - -+/* -+ * Remove a struct buffered_data from the list of outgoing data. -+ * A struct buffered_data related to a request having caused watch events to be -+ * sent is kept until all those events have been written out. -+ * Each watch event is referencing the related request via pend.req, while the -+ * number of watch events caused by a request is kept in pend.ref.event_cnt -+ * (those two cases are mutually exclusive, so the two fields can share memory -+ * via a union). -+ * The struct buffered_data is freed only if no related watch event is -+ * referencing it. The related return data can be freed right away. -+ */ - static void free_buffered_data(struct buffered_data *out, - struct connection *conn) - { - struct buffered_data *req; - - list_del(&out->list); -+ out->on_out_list = false; - - /* - * Update conn->timeout_msec with the next found timeout value in the -@@ -244,6 +257,30 @@ static void free_buffered_data(struct buffered_data *out, - } - } - -+ if (out->hdr.msg.type == XS_WATCH_EVENT) { -+ req = out->pend.req; -+ if (req) { -+ req->pend.ref.event_cnt--; -+ if (!req->pend.ref.event_cnt && !req->on_out_list) { -+ if (req->on_ref_list) { -+ domain_outstanding_domid_dec( -+ req->pend.ref.domid); -+ list_del(&req->list); -+ } -+ talloc_free(req); -+ } -+ } -+ } else if (out->pend.ref.event_cnt) { -+ /* Hang out off from conn. */ -+ talloc_steal(NULL, out); -+ if (out->buffer != out->default_buffer) -+ talloc_free(out->buffer); -+ list_add(&out->list, &conn->ref_list); -+ out->on_ref_list = true; -+ return; -+ } else -+ domain_outstanding_dec(conn); -+ - talloc_free(out); - } - -@@ -405,6 +442,7 @@ int delay_request(struct connection *conn, struct buffered_data *in, - static int destroy_conn(void *_conn) - { - struct connection *conn = _conn; -+ struct buffered_data *req; - - /* Flush outgoing if possible, but don't block. */ - if (!conn->domain) { -@@ -418,6 +456,11 @@ static int destroy_conn(void *_conn) - break; - close(conn->fd); - } -+ -+ conn_free_buffered_data(conn); -+ list_for_each_entry(req, &conn->ref_list, list) -+ req->on_ref_list = false; -+ - if (conn->target) - talloc_unlink(conn, conn->target); - list_del(&conn->list); -@@ -893,6 +936,8 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - - /* Queue for later transmission. */ - list_add_tail(&bdata->list, &conn->out_list); -+ bdata->on_out_list = true; -+ domain_outstanding_inc(conn); - } - - /* -@@ -900,7 +945,8 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - * As this is not directly related to the current command, errors can't be - * reported. - */ --void send_event(struct connection *conn, const char *path, const char *token) -+void send_event(struct buffered_data *req, struct connection *conn, -+ const char *path, const char *token) - { - struct buffered_data *bdata; - unsigned int len; -@@ -930,8 +976,13 @@ void send_event(struct connection *conn, const char *path, const char *token) - conn->timeout_msec = bdata->timeout_msec; - } - -+ bdata->pend.req = req; -+ if (req) -+ req->pend.ref.event_cnt++; -+ - /* Queue for later transmission. */ - list_add_tail(&bdata->list, &conn->out_list); -+ bdata->on_out_list = true; - } - - /* Some routines (write, mkdir, etc) just need a non-error return */ -@@ -1740,6 +1791,7 @@ static void handle_input(struct connection *conn) - return; - } - in = conn->in; -+ in->pend.ref.domid = conn->id; - - /* Not finished header yet? */ - if (in->inhdr) { -@@ -1808,6 +1860,7 @@ struct connection *new_connection(const struct interface_funcs *funcs) - new->is_stalled = false; - new->transaction_started = 0; - INIT_LIST_HEAD(&new->out_list); -+ INIT_LIST_HEAD(&new->ref_list); - INIT_LIST_HEAD(&new->watches); - INIT_LIST_HEAD(&new->transaction_list); - INIT_LIST_HEAD(&new->delayed); -@@ -2286,6 +2339,9 @@ static void usage(void) - " -t, --transaction limit the number of transaction allowed per domain,\n" - " -A, --perm-nb limit the number of permissions per node,\n" - " -M, --path-max limit the allowed Xenstore node path length,\n" -+" -Q, --quota = set the quota to the value , allowed\n" -+" quotas are:\n" -+" outstanding: number of outstanding requests\n" - " -w, --timeout = set the timeout in seconds for ,\n" - " allowed timeout candidates are:\n" - " watch-event: time a watch-event is kept pending\n" -@@ -2311,6 +2367,7 @@ static struct option options[] = { - { "transaction", 1, NULL, 't' }, - { "perm-nb", 1, NULL, 'A' }, - { "path-max", 1, NULL, 'M' }, -+ { "quota", 1, NULL, 'Q' }, - { "timeout", 1, NULL, 'w' }, - { "no-recovery", 0, NULL, 'R' }, - { "internal-db", 0, NULL, 'I' }, -@@ -2358,6 +2415,20 @@ static void set_timeout(const char *arg) - barf("unknown timeout \"%s\"\n", arg); - } - -+static void set_quota(const char *arg) -+{ -+ const char *eq = strchr(arg, '='); -+ int val; -+ -+ if (!eq) -+ barf("quotas must be specified via =\n"); -+ val = get_optval_int(eq + 1); -+ if (what_matches(arg, "outstanding")) -+ quota_req_outstanding = val; -+ else -+ barf("unknown quota \"%s\"\n", arg); -+} -+ - int main(int argc, char *argv[]) - { - int opt; -@@ -2372,8 +2443,8 @@ int main(int argc, char *argv[]) - orig_argc = argc; - orig_argv = argv; - -- while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:T:RVW:w:U", options, -- NULL)) != -1) { -+ while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:Q:T:RVW:w:U", -+ options, NULL)) != -1) { - switch (opt) { - case 'D': - no_domain_init = true; -@@ -2422,6 +2493,9 @@ int main(int argc, char *argv[]) - quota_max_path_len = min(XENSTORE_REL_PATH_MAX, - quota_max_path_len); - break; -+ case 'Q': -+ set_quota(optarg); -+ break; - case 'w': - set_timeout(optarg); - break; -@@ -2875,6 +2949,14 @@ static void add_buffered_data(struct buffered_data *bdata, - - /* Queue for later transmission. */ - list_add_tail(&bdata->list, &conn->out_list); -+ bdata->on_out_list = true; -+ /* -+ * Watch events are never "outstanding", but the request causing them -+ * are instead kept "outstanding" until all watch events caused by that -+ * request have been delivered. -+ */ -+ if (bdata->hdr.msg.type != XS_WATCH_EVENT) -+ domain_outstanding_inc(conn); - } - - void read_state_buffered_data(const void *ctx, struct connection *conn, -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index 8a81fc693f01..db09f463a657 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -56,6 +56,8 @@ struct xs_state_connection; - struct buffered_data - { - struct list_head list; -+ bool on_out_list; -+ bool on_ref_list; - - /* Are we still doing the header? */ - bool inhdr; -@@ -63,6 +65,17 @@ struct buffered_data - /* How far are we? */ - unsigned int used; - -+ /* Outstanding request accounting. */ -+ union { -+ /* ref is being used for requests. */ -+ struct { -+ unsigned int event_cnt; /* # of outstanding events. */ -+ unsigned int domid; /* domid of request. */ -+ } ref; -+ /* req is being used for watch events. */ -+ struct buffered_data *req; /* request causing event. */ -+ } pend; -+ - union { - struct xsd_sockmsg msg; - char raw[sizeof(struct xsd_sockmsg)]; -@@ -123,6 +136,9 @@ struct connection - struct list_head out_list; - uint64_t timeout_msec; - -+ /* Referenced requests no longer pending. */ -+ struct list_head ref_list; -+ - /* Transaction context for current request (NULL if none). */ - struct transaction *transaction; - -@@ -191,7 +207,8 @@ unsigned int get_string(const struct buffered_data *data, unsigned int offset); - - void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - const void *data, unsigned int len); --void send_event(struct connection *conn, const char *path, const char *token); -+void send_event(struct buffered_data *req, struct connection *conn, -+ const char *path, const char *token); - - /* Some routines (write, mkdir, etc) just need a non-error return */ - void send_ack(struct connection *conn, enum xsd_sockmsg_type type); -@@ -247,6 +264,7 @@ extern int dom0_domid; - extern int dom0_event; - extern int priv_domid; - extern int quota_nb_entry_per_domain; -+extern int quota_req_outstanding; - - extern unsigned int timeout_watch_event_msec; - -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index 93c4c1edcdd1..850085a92c76 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -78,6 +78,9 @@ struct domain - /* number of watch for this domain */ - int nbwatch; - -+ /* Number of outstanding requests. */ -+ int nboutstanding; -+ - /* write rate limit */ - wrl_creditt wrl_credit; /* [ -wrl_config_writecost, +_dburst ] */ - struct wrl_timestampt wrl_timestamp; -@@ -183,8 +186,12 @@ static bool domain_can_read(struct connection *conn) - { - struct xenstore_domain_interface *intf = conn->domain->interface; - -- if (domain_is_unprivileged(conn) && conn->domain->wrl_credit < 0) -- return false; -+ if (domain_is_unprivileged(conn)) { -+ if (conn->domain->wrl_credit < 0) -+ return false; -+ if (conn->domain->nboutstanding >= quota_req_outstanding) -+ return false; -+ } - - return (intf->req_cons != intf->req_prod); - } -@@ -331,7 +338,7 @@ static struct domain *alloc_domain(const void *context, unsigned int domid) - { - struct domain *domain; - -- domain = talloc(context, struct domain); -+ domain = talloc_zero(context, struct domain); - if (!domain) { - errno = ENOMEM; - return NULL; -@@ -392,9 +399,6 @@ static int new_domain(struct domain *domain, int port, bool restore) - domain->conn->domain = domain; - domain->conn->id = domain->domid; - -- domain->nbentry = 0; -- domain->nbwatch = 0; -- - return 0; - } - -@@ -938,6 +942,28 @@ int domain_watch(struct connection *conn) - : 0; - } - -+void domain_outstanding_inc(struct connection *conn) -+{ -+ if (!conn || !conn->domain) -+ return; -+ conn->domain->nboutstanding++; -+} -+ -+void domain_outstanding_dec(struct connection *conn) -+{ -+ if (!conn || !conn->domain) -+ return; -+ conn->domain->nboutstanding--; -+} -+ -+void domain_outstanding_domid_dec(unsigned int domid) -+{ -+ struct domain *d = find_domain_by_domid(domid); -+ -+ if (d) -+ d->nboutstanding--; -+} -+ - static wrl_creditt wrl_config_writecost = WRL_FACTOR; - static wrl_creditt wrl_config_rate = WRL_RATE * WRL_FACTOR; - static wrl_creditt wrl_config_dburst = WRL_DBURST * WRL_FACTOR; -diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h -index 1e929b8f8c6f..4f51b005291a 100644 ---- a/tools/xenstore/xenstored_domain.h -+++ b/tools/xenstore/xenstored_domain.h -@@ -64,6 +64,9 @@ int domain_entry(struct connection *conn); - void domain_watch_inc(struct connection *conn); - void domain_watch_dec(struct connection *conn); - int domain_watch(struct connection *conn); -+void domain_outstanding_inc(struct connection *conn); -+void domain_outstanding_dec(struct connection *conn); -+void domain_outstanding_domid_dec(unsigned int domid); - - /* Special node permission handling. */ - int set_perms_special(struct connection *conn, const char *name, -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index 205d9d8ea116..0755ffa375ba 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -142,6 +142,7 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, - struct node *node, bool exact, struct node_perms *perms) - { - struct connection *i; -+ struct buffered_data *req; - struct watch *watch; - - /* During transactions, don't fire watches, but queue them. */ -@@ -150,6 +151,8 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, - return; - } - -+ req = domain_is_unprivileged(conn) ? conn->in : NULL; -+ - /* Create an event for each watch. */ - list_for_each_entry(i, &connections, list) { - /* introduce/release domain watches */ -@@ -164,12 +167,12 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, - list_for_each_entry(watch, &i->watches, list) { - if (exact) { - if (streq(name, watch->node)) -- send_event(i, -+ send_event(req, i, - get_watch_path(watch, name), - watch->token); - } else { - if (is_child(name, watch->node)) -- send_event(i, -+ send_event(req, i, - get_watch_path(watch, name), - watch->token); - } -@@ -269,8 +272,12 @@ int do_watch(struct connection *conn, struct buffered_data *in) - trace_create(watch, "watch"); - send_ack(conn, XS_WATCH); - -- /* We fire once up front: simplifies clients and restart. */ -- send_event(conn, get_watch_path(watch, watch->node), watch->token); -+ /* -+ * We fire once up front: simplifies clients and restart. -+ * This event will not be linked to the XS_WATCH request. -+ */ -+ send_event(NULL, conn, get_watch_path(watch, watch->node), -+ watch->token); - - return 0; - } diff --git a/xsa326-4.16-xenstored-06.patch b/xsa326-4.16-xenstored-06.patch deleted file mode 100644 index b2be7ce..0000000 --- a/xsa326-4.16-xenstored-06.patch +++ /dev/null @@ -1,85 +0,0 @@ -From c8057cb483abf2cd4060b39616423e19283fbd0a Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:08 +0200 -Subject: tools/xenstore: don't buffer multiple identical watch events - -A guest not reading its Xenstore response buffer fast enough might -pile up lots of Xenstore watch events buffered. Reduce the generated -load by dropping new events which already have an identical copy -pending. - -The special events "@..." are excluded from that handling as there are -known use cases where the handler is relying on each event to be sent -individually. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 488d540f3a32..f1fa97b8cf50 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -916,6 +916,7 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - bdata->inhdr = true; - bdata->used = 0; - bdata->timeout_msec = 0; -+ bdata->watch_event = false; - - if (len <= DEFAULT_BUFFER_SIZE) - bdata->buffer = bdata->default_buffer; -@@ -948,7 +949,7 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - void send_event(struct buffered_data *req, struct connection *conn, - const char *path, const char *token) - { -- struct buffered_data *bdata; -+ struct buffered_data *bdata, *bd; - unsigned int len; - - len = strlen(path) + 1 + strlen(token) + 1; -@@ -970,12 +971,29 @@ void send_event(struct buffered_data *req, struct connection *conn, - bdata->hdr.msg.type = XS_WATCH_EVENT; - bdata->hdr.msg.len = len; - -+ /* -+ * Check whether an identical event is pending already. -+ * Special events are excluded from that check. -+ */ -+ if (path[0] != '@') { -+ list_for_each_entry(bd, &conn->out_list, list) { -+ if (bd->watch_event && bd->hdr.msg.len == len && -+ !memcmp(bdata->buffer, bd->buffer, len)) { -+ trace("dropping duplicate watch %s %s for domain %u\n", -+ path, token, conn->id); -+ talloc_free(bdata); -+ return; -+ } -+ } -+ } -+ - if (timeout_watch_event_msec && domain_is_unprivileged(conn)) { - bdata->timeout_msec = get_now_msec() + timeout_watch_event_msec; - if (!conn->timeout_msec) - conn->timeout_msec = bdata->timeout_msec; - } - -+ bdata->watch_event = true; - bdata->pend.req = req; - if (req) - req->pend.ref.event_cnt++; -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index db09f463a657..b9b50e81c7b4 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -62,6 +62,9 @@ struct buffered_data - /* Are we still doing the header? */ - bool inhdr; - -+ /* Is this a watch event? */ -+ bool watch_event; -+ - /* How far are we? */ - unsigned int used; - diff --git a/xsa326-4.16-xenstored-07.patch b/xsa326-4.16-xenstored-07.patch deleted file mode 100644 index dfcdc14..0000000 --- a/xsa326-4.16-xenstored-07.patch +++ /dev/null @@ -1,53 +0,0 @@ -From 5eac692b841633be3e85f0125c59fa02af103989 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:08 +0200 -Subject: tools/xenstore: fix connection->id usage - -Don't use conn->id for privilege checks, but domain_is_unprivileged(). - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_control.c b/tools/xenstore/xenstored_control.c -index 7b4300ef7777..adb8d51b043b 100644 ---- a/tools/xenstore/xenstored_control.c -+++ b/tools/xenstore/xenstored_control.c -@@ -891,7 +891,7 @@ int do_control(struct connection *conn, struct buffered_data *in) - unsigned int cmd, num, off; - char **vec = NULL; - -- if (conn->id != 0) -+ if (domain_is_unprivileged(conn)) - return EACCES; - - off = get_string(in, 0); -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index b9b50e81c7b4..b1a70488b989 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -123,7 +123,7 @@ struct connection - /* The index of pollfd in global pollfd array */ - int pollfd_idx; - -- /* Who am I? 0 for socket connections. */ -+ /* Who am I? Domid of connection. */ - unsigned int id; - - /* Is this connection ignored? */ -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index 54432907fc76..ee1b09031a3b 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -477,7 +477,8 @@ int do_transaction_start(struct connection *conn, struct buffered_data *in) - if (conn->transaction) - return EBUSY; - -- if (conn->id && conn->transaction_started > quota_max_transaction) -+ if (domain_is_unprivileged(conn) && -+ conn->transaction_started > quota_max_transaction) - return ENOSPC; - - /* Attach transaction to input for autofree until it's complete */ diff --git a/xsa326-4.16-xenstored-08.patch b/xsa326-4.16-xenstored-08.patch deleted file mode 100644 index 5aa12c8..0000000 --- a/xsa326-4.16-xenstored-08.patch +++ /dev/null @@ -1,326 +0,0 @@ -From f9f3171441b5fcb3339cf612400794fc26cd2ec2 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:08 +0200 -Subject: tools/xenstore: simplify and fix per domain node accounting - -The accounting of nodes can be simplified now that each connection -holds the associated domid. - -Fix the node accounting to cover nodes created for a domain before it -has been introduced. This requires to react properly to an allocation -failure inside domain_entry_inc() by returning an error code. - -Especially in error paths the node accounting has to be fixed in some -cases. - -This is part of XSA-326 / CVE-2022-42313. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index f1fa97b8cf50..692d863fce35 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -638,7 +638,7 @@ struct node *read_node(struct connection *conn, const void *ctx, - - /* Permissions are struct xs_permissions. */ - node->perms.p = hdr->perms; -- if (domain_adjust_node_perms(node)) { -+ if (domain_adjust_node_perms(conn, node)) { - talloc_free(node); - return NULL; - } -@@ -660,7 +660,7 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, - void *p; - struct xs_tdb_record_hdr *hdr; - -- if (domain_adjust_node_perms(node)) -+ if (domain_adjust_node_perms(conn, node)) - return errno; - - data.dsize = sizeof(*hdr) -@@ -1272,13 +1272,17 @@ static struct node *construct_node(struct connection *conn, const void *ctx, - return NULL; - } - --static int destroy_node(struct connection *conn, struct node *node) -+static void destroy_node_rm(struct node *node) - { - if (streq(node->name, "/")) - corrupt(NULL, "Destroying root node!"); - - tdb_delete(tdb_ctx, node->key); -+} - -+static int destroy_node(struct connection *conn, struct node *node) -+{ -+ destroy_node_rm(node); - domain_entry_dec(conn, node); - - /* -@@ -1328,8 +1332,12 @@ static struct node *create_node(struct connection *conn, const void *ctx, - goto err; - - /* Account for new node */ -- if (i->parent) -- domain_entry_inc(conn, i); -+ if (i->parent) { -+ if (domain_entry_inc(conn, i)) { -+ destroy_node_rm(i); -+ return NULL; -+ } -+ } - } - - return node; -@@ -1614,10 +1622,27 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - old_perms = node->perms; - domain_entry_dec(conn, node); - node->perms = perms; -- domain_entry_inc(conn, node); -+ if (domain_entry_inc(conn, node)) { -+ node->perms = old_perms; -+ /* -+ * This should never fail because we had a reference on the -+ * domain before and Xenstored is single-threaded. -+ */ -+ domain_entry_inc(conn, node); -+ return ENOMEM; -+ } -+ -+ if (write_node(conn, node, false)) { -+ int saved_errno = errno; - -- if (write_node(conn, node, false)) -+ domain_entry_dec(conn, node); -+ node->perms = old_perms; -+ /* No failure possible as above. */ -+ domain_entry_inc(conn, node); -+ -+ errno = saved_errno; - return errno; -+ } - - fire_watches(conn, in, name, node, false, &old_perms); - send_ack(conn, XS_SET_PERMS); -@@ -3122,7 +3147,9 @@ void read_state_node(const void *ctx, const void *state) - set_tdb_key(name, &key); - if (write_node_raw(NULL, &key, node, true)) - barf("write node error restoring node"); -- domain_entry_inc(&conn, node); -+ -+ if (domain_entry_inc(&conn, node)) -+ barf("node accounting error restoring node"); - - talloc_free(node); - } -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index 850085a92c76..260952e09096 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -16,6 +16,7 @@ - along with this program; If not, see . - */ - -+#include - #include - #include - #include -@@ -363,6 +364,18 @@ static struct domain *find_or_alloc_domain(const void *ctx, unsigned int domid) - return domain ? : alloc_domain(ctx, domid); - } - -+static struct domain *find_or_alloc_existing_domain(unsigned int domid) -+{ -+ struct domain *domain; -+ xc_dominfo_t dominfo; -+ -+ domain = find_domain_struct(domid); -+ if (!domain && get_domain_info(domid, &dominfo)) -+ domain = alloc_domain(NULL, domid); -+ -+ return domain; -+} -+ - static int new_domain(struct domain *domain, int port, bool restore) - { - int rc; -@@ -782,30 +795,28 @@ void domain_deinit(void) - xenevtchn_unbind(xce_handle, virq_port); - } - --void domain_entry_inc(struct connection *conn, struct node *node) -+int domain_entry_inc(struct connection *conn, struct node *node) - { - struct domain *d; -+ unsigned int domid; - - if (!conn) -- return; -+ return 0; - -- if (node->perms.p && node->perms.p[0].id != conn->id) { -- if (conn->transaction) { -- transaction_entry_inc(conn->transaction, -- node->perms.p[0].id); -- } else { -- d = find_domain_by_domid(node->perms.p[0].id); -- if (d) -- d->nbentry++; -- } -- } else if (conn->domain) { -- if (conn->transaction) { -- transaction_entry_inc(conn->transaction, -- conn->domain->domid); -- } else { -- conn->domain->nbentry++; -- } -+ domid = node->perms.p ? node->perms.p[0].id : conn->id; -+ -+ if (conn->transaction) { -+ transaction_entry_inc(conn->transaction, domid); -+ } else { -+ d = (domid == conn->id && conn->domain) ? conn->domain -+ : find_or_alloc_existing_domain(domid); -+ if (d) -+ d->nbentry++; -+ else -+ return ENOMEM; - } -+ -+ return 0; - } - - /* -@@ -841,7 +852,7 @@ static int chk_domain_generation(unsigned int domid, uint64_t gen) - * Remove permissions for no longer existing domains in order to avoid a new - * domain with the same domid inheriting the permissions. - */ --int domain_adjust_node_perms(struct node *node) -+int domain_adjust_node_perms(struct connection *conn, struct node *node) - { - unsigned int i; - int ret; -@@ -851,8 +862,14 @@ int domain_adjust_node_perms(struct node *node) - return errno; - - /* If the owner doesn't exist any longer give it to priv domain. */ -- if (!ret) -+ if (!ret) { -+ /* -+ * In theory we'd need to update the number of dom0 nodes here, -+ * but we could be called for a read of the node. So better -+ * avoid the risk to overflow the node count of dom0. -+ */ - node->perms.p[0].id = priv_domid; -+ } - - for (i = 1; i < node->perms.num; i++) { - if (node->perms.p[i].perms & XS_PERM_IGNORE) -@@ -871,25 +888,25 @@ int domain_adjust_node_perms(struct node *node) - void domain_entry_dec(struct connection *conn, struct node *node) - { - struct domain *d; -+ unsigned int domid; - - if (!conn) - return; - -- if (node->perms.p && node->perms.p[0].id != conn->id) { -- if (conn->transaction) { -- transaction_entry_dec(conn->transaction, -- node->perms.p[0].id); -- } else { -- d = find_domain_by_domid(node->perms.p[0].id); -- if (d && d->nbentry) -- d->nbentry--; -- } -- } else if (conn->domain && conn->domain->nbentry) { -- if (conn->transaction) { -- transaction_entry_dec(conn->transaction, -- conn->domain->domid); -+ domid = node->perms.p ? node->perms.p[0].id : conn->id; -+ -+ if (conn->transaction) { -+ transaction_entry_dec(conn->transaction, domid); -+ } else { -+ d = (domid == conn->id && conn->domain) ? conn->domain -+ : find_domain_struct(domid); -+ if (d) { -+ d->nbentry--; - } else { -- conn->domain->nbentry--; -+ errno = ENOENT; -+ corrupt(conn, -+ "Node \"%s\" owned by non-existing domain %u\n", -+ node->name, domid); - } - } - } -@@ -899,13 +916,23 @@ int domain_entry_fix(unsigned int domid, int num, bool update) - struct domain *d; - int cnt; - -- d = find_domain_by_domid(domid); -- if (!d) -- return 0; -+ if (update) { -+ d = find_domain_struct(domid); -+ assert(d); -+ } else { -+ /* -+ * We are called first with update == false in order to catch -+ * any error. So do a possible allocation and check for error -+ * only in this case, as in the case of update == true nothing -+ * can go wrong anymore as the allocation already happened. -+ */ -+ d = find_or_alloc_existing_domain(domid); -+ if (!d) -+ return -1; -+ } - - cnt = d->nbentry + num; -- if (cnt < 0) -- cnt = 0; -+ assert(cnt >= 0); - - if (update) - d->nbentry = cnt; -diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h -index 4f51b005291a..d6519904d831 100644 ---- a/tools/xenstore/xenstored_domain.h -+++ b/tools/xenstore/xenstored_domain.h -@@ -54,10 +54,10 @@ const char *get_implicit_path(const struct connection *conn); - bool domain_is_unprivileged(struct connection *conn); - - /* Remove node permissions for no longer existing domains. */ --int domain_adjust_node_perms(struct node *node); -+int domain_adjust_node_perms(struct connection *conn, struct node *node); - - /* Quota manipulation */ --void domain_entry_inc(struct connection *conn, struct node *); -+int domain_entry_inc(struct connection *conn, struct node *); - void domain_entry_dec(struct connection *conn, struct node *); - int domain_entry_fix(unsigned int domid, int num, bool update); - int domain_entry(struct connection *conn); -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index ee1b09031a3b..86caf6c398be 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -519,8 +519,12 @@ static int transaction_fix_domains(struct transaction *trans, bool update) - - list_for_each_entry(d, &trans->changed_domains, list) { - cnt = domain_entry_fix(d->domid, d->nbentry, update); -- if (!update && cnt >= quota_nb_entry_per_domain) -- return ENOSPC; -+ if (!update) { -+ if (cnt >= quota_nb_entry_per_domain) -+ return ENOSPC; -+ if (cnt < 0) -+ return ENOMEM; -+ } - } - - return 0; diff --git a/xsa326-4.16-xenstored-09.patch b/xsa326-4.16-xenstored-09.patch deleted file mode 100644 index e2c163c..0000000 --- a/xsa326-4.16-xenstored-09.patch +++ /dev/null @@ -1,244 +0,0 @@ -From 71aac6f7e89d5c101adb9e82eea7031e16d34e46 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:09 +0200 -Subject: tools/xenstore: limit max number of nodes accessed in a transaction - -Today a guest is free to access as many nodes in a single transaction -as it wants. This can lead to unbounded memory consumption in Xenstore -as there is the need to keep track of all nodes having been accessed -during a transaction. - -In oxenstored the number of requests in a transaction is being limited -via a quota maxrequests (default is 1024). As multiple accesses of a -node are not problematic in C Xenstore, limit the number of accessed -nodes. - -In order to let read_node() detect a quota error in case too many nodes -are being accessed, check the return value of access_node() and return -NULL in case an error has been seen. Introduce __must_check and add it -to the access_node() prototype. - -This is part of XSA-326 / CVE-2022-42314. - -Reported-by: Julien Grall -Suggested-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/include/xen-tools/libs.h b/tools/include/xen-tools/libs.h -index a16e0c380709..bafc90e2f603 100644 ---- a/tools/include/xen-tools/libs.h -+++ b/tools/include/xen-tools/libs.h -@@ -63,4 +63,8 @@ - #define ROUNDUP(_x,_w) (((unsigned long)(_x)+(1UL<<(_w))-1) & ~((1UL<<(_w))-1)) - #endif - -+#ifndef __must_check -+#define __must_check __attribute__((__warn_unused_result__)) -+#endif -+ - #endif /* __XEN_TOOLS_LIBS__ */ -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 692d863fce35..f835aa1b2f1f 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -106,6 +106,7 @@ int quota_nb_watch_per_domain = 128; - int quota_max_entry_size = 2048; /* 2K */ - int quota_max_transaction = 10; - int quota_nb_perms_per_node = 5; -+int quota_trans_nodes = 1024; - int quota_max_path_len = XENSTORE_REL_PATH_MAX; - int quota_req_outstanding = 20; - -@@ -595,6 +596,7 @@ struct node *read_node(struct connection *conn, const void *ctx, - TDB_DATA key, data; - struct xs_tdb_record_hdr *hdr; - struct node *node; -+ int err; - - node = talloc(ctx, struct node); - if (!node) { -@@ -616,14 +618,13 @@ struct node *read_node(struct connection *conn, const void *ctx, - if (data.dptr == NULL) { - if (tdb_error(tdb_ctx) == TDB_ERR_NOEXIST) { - node->generation = NO_GENERATION; -- access_node(conn, node, NODE_ACCESS_READ, NULL); -- errno = ENOENT; -+ err = access_node(conn, node, NODE_ACCESS_READ, NULL); -+ errno = err ? : ENOENT; - } else { - log("TDB error on read: %s", tdb_errorstr(tdb_ctx)); - errno = EIO; - } -- talloc_free(node); -- return NULL; -+ goto error; - } - - node->parent = NULL; -@@ -638,19 +639,36 @@ struct node *read_node(struct connection *conn, const void *ctx, - - /* Permissions are struct xs_permissions. */ - node->perms.p = hdr->perms; -- if (domain_adjust_node_perms(conn, node)) { -- talloc_free(node); -- return NULL; -- } -+ if (domain_adjust_node_perms(conn, node)) -+ goto error; - - /* Data is binary blob (usually ascii, no nul). */ - node->data = node->perms.p + hdr->num_perms; - /* Children is strings, nul separated. */ - node->children = node->data + node->datalen; - -- access_node(conn, node, NODE_ACCESS_READ, NULL); -+ if (access_node(conn, node, NODE_ACCESS_READ, NULL)) -+ goto error; - - return node; -+ -+ error: -+ err = errno; -+ talloc_free(node); -+ errno = err; -+ return NULL; -+} -+ -+static bool read_node_can_propagate_errno(void) -+{ -+ /* -+ * 2 error cases for read_node() can always be propagated up: -+ * ENOMEM, because this has nothing to do with the node being in the -+ * data base or not, but is caused by a general lack of memory. -+ * ENOSPC, because this is related to hitting quota limits which need -+ * to be respected. -+ */ -+ return errno == ENOMEM || errno == ENOSPC; - } - - int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, -@@ -767,7 +785,7 @@ static int ask_parents(struct connection *conn, const void *ctx, - node = read_node(conn, ctx, name); - if (node) - break; -- if (errno == ENOMEM) -+ if (read_node_can_propagate_errno()) - return errno; - } while (!streq(name, "/")); - -@@ -829,7 +847,7 @@ static struct node *get_node(struct connection *conn, - } - } - /* Clean up errno if they weren't supposed to know. */ -- if (!node && errno != ENOMEM) -+ if (!node && !read_node_can_propagate_errno()) - errno = errno_from_parents(conn, ctx, name, errno, perm); - return node; - } -@@ -1235,7 +1253,7 @@ static struct node *construct_node(struct connection *conn, const void *ctx, - - /* If parent doesn't exist, create it. */ - parent = read_node(conn, parentname, parentname); -- if (!parent) -+ if (!parent && errno == ENOENT) - parent = construct_node(conn, ctx, parentname); - if (!parent) - return NULL; -@@ -1509,7 +1527,7 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, - - parent = read_node(conn, ctx, parentname); - if (!parent) -- return (errno == ENOMEM) ? ENOMEM : EINVAL; -+ return read_node_can_propagate_errno() ? errno : EINVAL; - node->parent = parent; - - return delete_node(conn, ctx, parent, node, false); -@@ -1539,7 +1557,7 @@ static int do_rm(struct connection *conn, struct buffered_data *in) - return 0; - } - /* Restore errno, just in case. */ -- if (errno != ENOMEM) -+ if (!read_node_can_propagate_errno()) - errno = ENOENT; - } - return errno; -@@ -2384,6 +2402,8 @@ static void usage(void) - " -M, --path-max limit the allowed Xenstore node path length,\n" - " -Q, --quota = set the quota to the value , allowed\n" - " quotas are:\n" -+" transaction-nodes: number of accessed node per\n" -+" transaction\n" - " outstanding: number of outstanding requests\n" - " -w, --timeout = set the timeout in seconds for ,\n" - " allowed timeout candidates are:\n" -@@ -2468,6 +2488,8 @@ static void set_quota(const char *arg) - val = get_optval_int(eq + 1); - if (what_matches(arg, "outstanding")) - quota_req_outstanding = val; -+ else if (what_matches(arg, "transaction-nodes")) -+ quota_trans_nodes = val; - else - barf("unknown quota \"%s\"\n", arg); - } -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index b1a70488b989..245f9258235f 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -268,6 +268,7 @@ extern int dom0_event; - extern int priv_domid; - extern int quota_nb_entry_per_domain; - extern int quota_req_outstanding; -+extern int quota_trans_nodes; - - extern unsigned int timeout_watch_event_msec; - -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index 86caf6c398be..7bd41eb475e3 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -156,6 +156,9 @@ struct transaction - /* Connection-local identifier for this transaction. */ - uint32_t id; - -+ /* Node counter. */ -+ unsigned int nodes; -+ - /* Generation when transaction started. */ - uint64_t generation; - -@@ -260,6 +263,11 @@ int access_node(struct connection *conn, struct node *node, - - i = find_accessed_node(trans, node->name); - if (!i) { -+ if (trans->nodes >= quota_trans_nodes && -+ domain_is_unprivileged(conn)) { -+ ret = ENOSPC; -+ goto err; -+ } - i = talloc_zero(trans, struct accessed_node); - if (!i) - goto nomem; -@@ -297,6 +305,7 @@ int access_node(struct connection *conn, struct node *node, - i->ta_node = true; - } - } -+ trans->nodes++; - list_add_tail(&i->list, &trans->accessed); - } - -diff --git a/tools/xenstore/xenstored_transaction.h b/tools/xenstore/xenstored_transaction.h -index 0093cac807e3..e3cbd6b23095 100644 ---- a/tools/xenstore/xenstored_transaction.h -+++ b/tools/xenstore/xenstored_transaction.h -@@ -39,8 +39,8 @@ void transaction_entry_inc(struct transaction *trans, unsigned int domid); - void transaction_entry_dec(struct transaction *trans, unsigned int domid); - - /* This node was accessed. */ --int access_node(struct connection *conn, struct node *node, -- enum node_access_type type, TDB_DATA *key); -+int __must_check access_node(struct connection *conn, struct node *node, -+ enum node_access_type type, TDB_DATA *key); - - /* Queue watches for a modified node. */ - void queue_watches(struct connection *conn, const char *name, bool watch_exact); diff --git a/xsa326-4.16-xenstored-10.patch b/xsa326-4.16-xenstored-10.patch deleted file mode 100644 index 30e6b50..0000000 --- a/xsa326-4.16-xenstored-10.patch +++ /dev/null @@ -1,88 +0,0 @@ -From 90013d6a735491a7b93a6832eb2a51e5633254f5 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:09 +0200 -Subject: tools/xenstore: move the call of setup_structure() to dom0 - introduction - -Setting up the basic structure when introducing dom0 has the advantage -to be able to add proper node memory accounting for the added nodes -later. - -This makes it possible to do proper node accounting, too. - -An additional requirement to make that work fine is to correct the -owner of the created nodes to be dom0_domid instead of domid 0. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Acked-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index f835aa1b2f1f..5171d34c947e 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -2039,7 +2039,8 @@ static int tdb_flags; - static void manual_node(const char *name, const char *child) - { - struct node *node; -- struct xs_permissions perms = { .id = 0, .perms = XS_PERM_NONE }; -+ struct xs_permissions perms = { .id = dom0_domid, -+ .perms = XS_PERM_NONE }; - - node = talloc_zero(NULL, struct node); - if (!node) -@@ -2078,7 +2079,7 @@ static void tdb_logger(TDB_CONTEXT *tdb, int level, const char * fmt, ...) - } - } - --static void setup_structure(bool live_update) -+void setup_structure(bool live_update) - { - char *tdbname; - -@@ -2101,6 +2102,7 @@ static void setup_structure(bool live_update) - manual_node("/", "tool"); - manual_node("/tool", "xenstored"); - manual_node("/tool/xenstored", NULL); -+ domain_entry_fix(dom0_domid, 3, true); - } - - check_store(); -@@ -2614,9 +2616,6 @@ int main(int argc, char *argv[]) - - init_pipe(reopen_log_pipe); - -- /* Setup the database */ -- setup_structure(live_update); -- - /* Listen to hypervisor. */ - if (!no_domain_init && !live_update) { - domain_init(-1); -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index 245f9258235f..2c77ec7ee0f4 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -231,6 +231,7 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, - struct node *read_node(struct connection *conn, const void *ctx, - const char *name); - -+void setup_structure(bool live_update); - struct connection *new_connection(const struct interface_funcs *funcs); - struct connection *get_connection_by_id(unsigned int conn_id); - void ignore_connection(struct connection *conn); -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index 260952e09096..f04b7aae8a32 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -470,6 +470,9 @@ static struct domain *introduce_domain(const void *ctx, - } - domain->interface = interface; - -+ if (is_master_domain) -+ setup_structure(restore); -+ - /* Now domain belongs to its connection. */ - talloc_steal(domain->conn, domain); - diff --git a/xsa326-4.16-xenstored-11.patch b/xsa326-4.16-xenstored-11.patch deleted file mode 100644 index 49f1497..0000000 --- a/xsa326-4.16-xenstored-11.patch +++ /dev/null @@ -1,280 +0,0 @@ -From 6af17b8bf52b9dfdc6a5ecd3efbcea9fddd57d91 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:09 +0200 -Subject: tools/xenstore: add infrastructure to keep track of per domain memory - usage - -The amount of memory a domain can consume in Xenstore is limited by -various quota today, but even with sane quota a domain can still -consume rather large memory quantities. - -Add the infrastructure for keeping track of the amount of memory a -domain is consuming in Xenstore. Note that this is only the memory a -domain has direct control over, so any internal administration data -needed by Xenstore only is not being accounted for. - -There are two quotas defined: a soft quota which will result in a -warning issued via syslog() when it is exceeded, and a hard quota -resulting in a stop of accepting further requests or watch events as -long as the hard quota would be violated by accepting those. - -Setting any of those quotas to 0 will disable it. - -As default values use 2MB per domain for the soft limit (this basically -covers the allowed case to create 1000 nodes needing 2kB each), and -2.5MB for the hard limit. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 5171d34c947e..b2bf6740d430 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -109,6 +109,8 @@ int quota_nb_perms_per_node = 5; - int quota_trans_nodes = 1024; - int quota_max_path_len = XENSTORE_REL_PATH_MAX; - int quota_req_outstanding = 20; -+int quota_memory_per_domain_soft = 2 * 1024 * 1024; /* 2 MB */ -+int quota_memory_per_domain_hard = 2 * 1024 * 1024 + 512 * 1024; /* 2.5 MB */ - - unsigned int timeout_watch_event_msec = 20000; - -@@ -2406,7 +2408,14 @@ static void usage(void) - " quotas are:\n" - " transaction-nodes: number of accessed node per\n" - " transaction\n" -+" memory: total used memory per domain for nodes,\n" -+" transactions, watches and requests, above\n" -+" which Xenstore will stop talking to domain\n" - " outstanding: number of outstanding requests\n" -+" -q, --quota-soft = set a soft quota to the value ,\n" -+" causing a warning to be issued via syslog() if the\n" -+" limit is violated, allowed quotas are:\n" -+" memory: see above\n" - " -w, --timeout = set the timeout in seconds for ,\n" - " allowed timeout candidates are:\n" - " watch-event: time a watch-event is kept pending\n" -@@ -2433,6 +2442,7 @@ static struct option options[] = { - { "perm-nb", 1, NULL, 'A' }, - { "path-max", 1, NULL, 'M' }, - { "quota", 1, NULL, 'Q' }, -+ { "quota-soft", 1, NULL, 'q' }, - { "timeout", 1, NULL, 'w' }, - { "no-recovery", 0, NULL, 'R' }, - { "internal-db", 0, NULL, 'I' }, -@@ -2480,7 +2490,7 @@ static void set_timeout(const char *arg) - barf("unknown timeout \"%s\"\n", arg); - } - --static void set_quota(const char *arg) -+static void set_quota(const char *arg, bool soft) - { - const char *eq = strchr(arg, '='); - int val; -@@ -2488,11 +2498,16 @@ static void set_quota(const char *arg) - if (!eq) - barf("quotas must be specified via =\n"); - val = get_optval_int(eq + 1); -- if (what_matches(arg, "outstanding")) -+ if (what_matches(arg, "outstanding") && !soft) - quota_req_outstanding = val; -- else if (what_matches(arg, "transaction-nodes")) -+ else if (what_matches(arg, "transaction-nodes") && !soft) - quota_trans_nodes = val; -- else -+ else if (what_matches(arg, "memory")) { -+ if (soft) -+ quota_memory_per_domain_soft = val; -+ else -+ quota_memory_per_domain_hard = val; -+ } else - barf("unknown quota \"%s\"\n", arg); - } - -@@ -2510,7 +2525,7 @@ int main(int argc, char *argv[]) - orig_argc = argc; - orig_argv = argv; - -- while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:Q:T:RVW:w:U", -+ while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:Q:q:T:RVW:w:U", - options, NULL)) != -1) { - switch (opt) { - case 'D': -@@ -2561,7 +2576,10 @@ int main(int argc, char *argv[]) - quota_max_path_len); - break; - case 'Q': -- set_quota(optarg); -+ set_quota(optarg, false); -+ break; -+ case 'q': -+ set_quota(optarg, true); - break; - case 'w': - set_timeout(optarg); -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index 2c77ec7ee0f4..373af18297bf 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -270,6 +270,8 @@ extern int priv_domid; - extern int quota_nb_entry_per_domain; - extern int quota_req_outstanding; - extern int quota_trans_nodes; -+extern int quota_memory_per_domain_soft; -+extern int quota_memory_per_domain_hard; - - extern unsigned int timeout_watch_event_msec; - -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index f04b7aae8a32..94fd561e9de4 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -76,6 +76,13 @@ struct domain - /* number of entry from this domain in the store */ - int nbentry; - -+ /* Amount of memory allocated for this domain. */ -+ int memory; -+ bool soft_quota_reported; -+ bool hard_quota_reported; -+ time_t mem_last_msg; -+#define MEM_WARN_MINTIME_SEC 10 -+ - /* number of watch for this domain */ - int nbwatch; - -@@ -192,6 +199,9 @@ static bool domain_can_read(struct connection *conn) - return false; - if (conn->domain->nboutstanding >= quota_req_outstanding) - return false; -+ if (conn->domain->memory >= quota_memory_per_domain_hard && -+ quota_memory_per_domain_hard) -+ return false; - } - - return (intf->req_cons != intf->req_prod); -@@ -950,6 +960,89 @@ int domain_entry(struct connection *conn) - : 0; - } - -+static bool domain_chk_quota(struct domain *domain, int mem) -+{ -+ time_t now; -+ -+ if (!domain || !domid_is_unprivileged(domain->domid) || -+ (domain->conn && domain->conn->is_ignored)) -+ return false; -+ -+ now = time(NULL); -+ -+ if (mem >= quota_memory_per_domain_hard && -+ quota_memory_per_domain_hard) { -+ if (domain->hard_quota_reported) -+ return true; -+ syslog(LOG_ERR, "Domain %u exceeds hard memory quota, Xenstore interface to domain stalled\n", -+ domain->domid); -+ domain->mem_last_msg = now; -+ domain->hard_quota_reported = true; -+ return true; -+ } -+ -+ if (now - domain->mem_last_msg >= MEM_WARN_MINTIME_SEC) { -+ if (domain->hard_quota_reported) { -+ domain->mem_last_msg = now; -+ domain->hard_quota_reported = false; -+ syslog(LOG_INFO, "Domain %u below hard memory quota again\n", -+ domain->domid); -+ } -+ if (mem >= quota_memory_per_domain_soft && -+ quota_memory_per_domain_soft && -+ !domain->soft_quota_reported) { -+ domain->mem_last_msg = now; -+ domain->soft_quota_reported = true; -+ syslog(LOG_WARNING, "Domain %u exceeds soft memory quota\n", -+ domain->domid); -+ } -+ if (mem < quota_memory_per_domain_soft && -+ domain->soft_quota_reported) { -+ domain->mem_last_msg = now; -+ domain->soft_quota_reported = false; -+ syslog(LOG_INFO, "Domain %u below soft memory quota again\n", -+ domain->domid); -+ } -+ -+ } -+ -+ return false; -+} -+ -+int domain_memory_add(unsigned int domid, int mem, bool no_quota_check) -+{ -+ struct domain *domain; -+ -+ domain = find_domain_struct(domid); -+ if (domain) { -+ /* -+ * domain_chk_quota() will print warning and also store whether -+ * the soft/hard quota has been hit. So check no_quota_check -+ * *after*. -+ */ -+ if (domain_chk_quota(domain, domain->memory + mem) && -+ !no_quota_check) -+ return ENOMEM; -+ domain->memory += mem; -+ } else { -+ /* -+ * The domain the memory is to be accounted for should always -+ * exist, as accounting is done either for a domain related to -+ * the current connection, or for the domain owning a node -+ * (which is always existing, as the owner of the node is -+ * tested to exist and replaced by domid 0 if not). -+ * So not finding the related domain MUST be an error in the -+ * data base. -+ */ -+ errno = ENOENT; -+ corrupt(NULL, "Accounting called for non-existing domain %u\n", -+ domid); -+ return ENOENT; -+ } -+ -+ return 0; -+} -+ - void domain_watch_inc(struct connection *conn) - { - if (!conn || !conn->domain) -diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h -index d6519904d831..633c9a0a0a1f 100644 ---- a/tools/xenstore/xenstored_domain.h -+++ b/tools/xenstore/xenstored_domain.h -@@ -61,6 +61,26 @@ int domain_entry_inc(struct connection *conn, struct node *); - void domain_entry_dec(struct connection *conn, struct node *); - int domain_entry_fix(unsigned int domid, int num, bool update); - int domain_entry(struct connection *conn); -+int domain_memory_add(unsigned int domid, int mem, bool no_quota_check); -+ -+/* -+ * domain_memory_add_chk(): to be used when memory quota should be checked. -+ * Not to be used when specifying a negative mem value, as lowering the used -+ * memory should always be allowed. -+ */ -+static inline int domain_memory_add_chk(unsigned int domid, int mem) -+{ -+ return domain_memory_add(domid, mem, false); -+} -+/* -+ * domain_memory_add_nochk(): to be used when memory quota should not be -+ * checked, e.g. when lowering memory usage, or in an error case for undoing -+ * a previous memory adjustment. -+ */ -+static inline void domain_memory_add_nochk(unsigned int domid, int mem) -+{ -+ domain_memory_add(domid, mem, true); -+} - void domain_watch_inc(struct connection *conn); - void domain_watch_dec(struct connection *conn); - int domain_watch(struct connection *conn); diff --git a/xsa326-4.16-xenstored-12.patch b/xsa326-4.16-xenstored-12.patch deleted file mode 100644 index 517f2a0..0000000 --- a/xsa326-4.16-xenstored-12.patch +++ /dev/null @@ -1,76 +0,0 @@ -From ae7042f024af7584251f776a12d9bb24d13fecaf Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:09 +0200 -Subject: tools/xenstore: add memory accounting for responses - -Add the memory accounting for queued responses. - -In case adding a watch event for a guest is causing the hard memory -quota of that guest to be violated, the event is dropped. This will -ensure that it is impossible to drive another guest past its memory -quota by generating insane amounts of events for that guest. This is -especially important for protecting driver domains from that attack -vector. - -This is part of XSA-326 / CVE-2022-42315. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index b2bf6740d430..ecab6cfbbe15 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -260,6 +260,8 @@ static void free_buffered_data(struct buffered_data *out, - } - } - -+ domain_memory_add_nochk(conn->id, -out->hdr.msg.len - sizeof(out->hdr)); -+ - if (out->hdr.msg.type == XS_WATCH_EVENT) { - req = out->pend.req; - if (req) { -@@ -938,11 +940,14 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - bdata->timeout_msec = 0; - bdata->watch_event = false; - -- if (len <= DEFAULT_BUFFER_SIZE) -+ if (len <= DEFAULT_BUFFER_SIZE) { - bdata->buffer = bdata->default_buffer; -- else { -+ /* Don't check quota, path might be used for returning error. */ -+ domain_memory_add_nochk(conn->id, len + sizeof(bdata->hdr)); -+ } else { - bdata->buffer = talloc_array(bdata, char, len); -- if (!bdata->buffer) { -+ if (!bdata->buffer || -+ domain_memory_add_chk(conn->id, len + sizeof(bdata->hdr))) { - send_error(conn, ENOMEM); - return; - } -@@ -1007,6 +1012,11 @@ void send_event(struct buffered_data *req, struct connection *conn, - } - } - -+ if (domain_memory_add_chk(conn->id, len + sizeof(bdata->hdr))) { -+ talloc_free(bdata); -+ return; -+ } -+ - if (timeout_watch_event_msec && domain_is_unprivileged(conn)) { - bdata->timeout_msec = get_now_msec() + timeout_watch_event_msec; - if (!conn->timeout_msec) -@@ -3039,6 +3049,12 @@ static void add_buffered_data(struct buffered_data *bdata, - */ - if (bdata->hdr.msg.type != XS_WATCH_EVENT) - domain_outstanding_inc(conn); -+ /* -+ * We are restoring the state after Live-Update and the new quota may -+ * be smaller. So ignore it. The limit will be applied for any resource -+ * after the state has been fully restored. -+ */ -+ domain_memory_add_nochk(conn->id, len + sizeof(bdata->hdr)); - } - - void read_state_buffered_data(const void *ctx, struct connection *conn, diff --git a/xsa326-4.16-xenstored-13.patch b/xsa326-4.16-xenstored-13.patch deleted file mode 100644 index c37dd1e..0000000 --- a/xsa326-4.16-xenstored-13.patch +++ /dev/null @@ -1,89 +0,0 @@ -From 4628ae0a56b037dcdc8a3e42c543c5b9fd9990cf Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:10 +0200 -Subject: tools/xenstore: add memory accounting for watches - -Add the memory accounting for registered watches. - -When a socket connection is destroyed, the associated watches are -removed, too. In order to keep memory accounting correct the watches -must be removed explicitly via a call of conn_delete_all_watches() from -destroy_conn(). - -This is part of XSA-326 / CVE-2022-42315. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index ecab6cfbbe15..d86942f5aa77 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -463,6 +463,7 @@ static int destroy_conn(void *_conn) - } - - conn_free_buffered_data(conn); -+ conn_delete_all_watches(conn); - list_for_each_entry(req, &conn->ref_list, list) - req->on_ref_list = false; - -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index 0755ffa375ba..fdf9b2d653a0 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -211,7 +211,7 @@ static int check_watch_path(struct connection *conn, const void *ctx, - } - - static struct watch *add_watch(struct connection *conn, char *path, char *token, -- bool relative) -+ bool relative, bool no_quota_check) - { - struct watch *watch; - -@@ -222,6 +222,9 @@ static struct watch *add_watch(struct connection *conn, char *path, char *token, - watch->token = talloc_strdup(watch, token); - if (!watch->node || !watch->token) - goto nomem; -+ if (domain_memory_add(conn->id, strlen(path) + strlen(token), -+ no_quota_check)) -+ goto nomem; - - if (relative) - watch->relative_path = get_implicit_path(conn); -@@ -265,7 +268,7 @@ int do_watch(struct connection *conn, struct buffered_data *in) - if (domain_watch(conn) > quota_nb_watch_per_domain) - return E2BIG; - -- watch = add_watch(conn, vec[0], vec[1], relative); -+ watch = add_watch(conn, vec[0], vec[1], relative, false); - if (!watch) - return errno; - -@@ -296,6 +299,8 @@ int do_unwatch(struct connection *conn, struct buffered_data *in) - list_for_each_entry(watch, &conn->watches, list) { - if (streq(watch->node, node) && streq(watch->token, vec[1])) { - list_del(&watch->list); -+ domain_memory_add_nochk(conn->id, -strlen(watch->node) - -+ strlen(watch->token)); - talloc_free(watch); - domain_watch_dec(conn); - send_ack(conn, XS_UNWATCH); -@@ -311,6 +316,8 @@ void conn_delete_all_watches(struct connection *conn) - - while ((watch = list_top(&conn->watches, struct watch, list))) { - list_del(&watch->list); -+ domain_memory_add_nochk(conn->id, -strlen(watch->node) - -+ strlen(watch->token)); - talloc_free(watch); - domain_watch_dec(conn); - } -@@ -373,7 +380,7 @@ void read_state_watch(const void *ctx, const void *state) - if (!path) - barf("allocation error for read watch"); - -- if (!add_watch(conn, path, token, relative)) -+ if (!add_watch(conn, path, token, relative, true)) - barf("error adding watch"); - } - diff --git a/xsa326-4.16-xenstored-14.patch b/xsa326-4.16-xenstored-14.patch deleted file mode 100644 index df898c4..0000000 --- a/xsa326-4.16-xenstored-14.patch +++ /dev/null @@ -1,334 +0,0 @@ -From b8bd74e5e962955211ab0c5c1924ebf2bb526799 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:10 +0200 -Subject: tools/xenstore: add memory accounting for nodes - -Add the memory accounting for Xenstore nodes. In order to make this -not too complicated allow for some sloppiness when writing nodes. Any -hard quota violation will result in no further requests to be accepted. - -This is part of XSA-326 / CVE-2022-42315. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index d86942f5aa77..16504de42017 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -591,6 +591,117 @@ void set_tdb_key(const char *name, TDB_DATA *key) - key->dsize = strlen(name); - } - -+static void get_acc_data(TDB_DATA *key, struct node_account_data *acc) -+{ -+ TDB_DATA old_data; -+ struct xs_tdb_record_hdr *hdr; -+ -+ if (acc->memory < 0) { -+ old_data = tdb_fetch(tdb_ctx, *key); -+ /* No check for error, as the node might not exist. */ -+ if (old_data.dptr == NULL) { -+ acc->memory = 0; -+ } else { -+ hdr = (void *)old_data.dptr; -+ acc->memory = old_data.dsize; -+ acc->domid = hdr->perms[0].id; -+ } -+ talloc_free(old_data.dptr); -+ } -+} -+ -+/* -+ * Per-transaction nodes need to be accounted for the transaction owner. -+ * Those nodes are stored in the data base with the transaction generation -+ * count prepended (e.g. 123/local/domain/...). So testing for the node's -+ * key not to start with "/" is sufficient. -+ */ -+static unsigned int get_acc_domid(struct connection *conn, TDB_DATA *key, -+ unsigned int domid) -+{ -+ return (!conn || key->dptr[0] == '/') ? domid : conn->id; -+} -+ -+int do_tdb_write(struct connection *conn, TDB_DATA *key, TDB_DATA *data, -+ struct node_account_data *acc, bool no_quota_check) -+{ -+ struct xs_tdb_record_hdr *hdr = (void *)data->dptr; -+ struct node_account_data old_acc = {}; -+ unsigned int old_domid, new_domid; -+ int ret; -+ -+ if (!acc) -+ old_acc.memory = -1; -+ else -+ old_acc = *acc; -+ -+ get_acc_data(key, &old_acc); -+ old_domid = get_acc_domid(conn, key, old_acc.domid); -+ new_domid = get_acc_domid(conn, key, hdr->perms[0].id); -+ -+ /* -+ * Don't check for ENOENT, as we want to be able to switch orphaned -+ * nodes to new owners. -+ */ -+ if (old_acc.memory) -+ domain_memory_add_nochk(old_domid, -+ -old_acc.memory - key->dsize); -+ ret = domain_memory_add(new_domid, data->dsize + key->dsize, -+ no_quota_check); -+ if (ret) { -+ /* Error path, so no quota check. */ -+ if (old_acc.memory) -+ domain_memory_add_nochk(old_domid, -+ old_acc.memory + key->dsize); -+ return ret; -+ } -+ -+ /* TDB should set errno, but doesn't even set ecode AFAICT. */ -+ if (tdb_store(tdb_ctx, *key, *data, TDB_REPLACE) != 0) { -+ domain_memory_add_nochk(new_domid, -data->dsize - key->dsize); -+ /* Error path, so no quota check. */ -+ if (old_acc.memory) -+ domain_memory_add_nochk(old_domid, -+ old_acc.memory + key->dsize); -+ errno = EIO; -+ return errno; -+ } -+ -+ if (acc) { -+ /* Don't use new_domid, as it might be a transaction node. */ -+ acc->domid = hdr->perms[0].id; -+ acc->memory = data->dsize; -+ } -+ -+ return 0; -+} -+ -+int do_tdb_delete(struct connection *conn, TDB_DATA *key, -+ struct node_account_data *acc) -+{ -+ struct node_account_data tmp_acc; -+ unsigned int domid; -+ -+ if (!acc) { -+ acc = &tmp_acc; -+ acc->memory = -1; -+ } -+ -+ get_acc_data(key, acc); -+ -+ if (tdb_delete(tdb_ctx, *key)) { -+ errno = EIO; -+ return errno; -+ } -+ -+ if (acc->memory) { -+ domid = get_acc_domid(conn, key, acc->domid); -+ domain_memory_add_nochk(domid, -acc->memory - key->dsize); -+ } -+ -+ return 0; -+} -+ - /* - * If it fails, returns NULL and sets errno. - * Temporary memory allocations will be done with ctx. -@@ -644,9 +755,15 @@ struct node *read_node(struct connection *conn, const void *ctx, - - /* Permissions are struct xs_permissions. */ - node->perms.p = hdr->perms; -+ node->acc.domid = node->perms.p[0].id; -+ node->acc.memory = data.dsize; - if (domain_adjust_node_perms(conn, node)) - goto error; - -+ /* If owner is gone reset currently accounted memory size. */ -+ if (node->acc.domid != node->perms.p[0].id) -+ node->acc.memory = 0; -+ - /* Data is binary blob (usually ascii, no nul). */ - node->data = node->perms.p + hdr->num_perms; - /* Children is strings, nul separated. */ -@@ -715,12 +832,9 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, - p += node->datalen; - memcpy(p, node->children, node->childlen); - -- /* TDB should set errno, but doesn't even set ecode AFAICT. */ -- if (tdb_store(tdb_ctx, *key, data, TDB_REPLACE) != 0) { -- corrupt(conn, "Write of %s failed", key->dptr); -- errno = EIO; -- return errno; -- } -+ if (do_tdb_write(conn, key, &data, &node->acc, no_quota_check)) -+ return EIO; -+ - return 0; - } - -@@ -1222,7 +1336,7 @@ static void delete_node_single(struct connection *conn, struct node *node) - if (access_node(conn, node, NODE_ACCESS_DELETE, &key)) - return; - -- if (tdb_delete(tdb_ctx, key) != 0) { -+ if (do_tdb_delete(conn, &key, &node->acc) != 0) { - corrupt(conn, "Could not delete '%s'", node->name); - return; - } -@@ -1295,6 +1409,7 @@ static struct node *construct_node(struct connection *conn, const void *ctx, - /* No children, no data */ - node->children = node->data = NULL; - node->childlen = node->datalen = 0; -+ node->acc.memory = 0; - node->parent = parent; - return node; - -@@ -1303,17 +1418,17 @@ static struct node *construct_node(struct connection *conn, const void *ctx, - return NULL; - } - --static void destroy_node_rm(struct node *node) -+static void destroy_node_rm(struct connection *conn, struct node *node) - { - if (streq(node->name, "/")) - corrupt(NULL, "Destroying root node!"); - -- tdb_delete(tdb_ctx, node->key); -+ do_tdb_delete(conn, &node->key, &node->acc); - } - - static int destroy_node(struct connection *conn, struct node *node) - { -- destroy_node_rm(node); -+ destroy_node_rm(conn, node); - domain_entry_dec(conn, node); - - /* -@@ -1365,7 +1480,7 @@ static struct node *create_node(struct connection *conn, const void *ctx, - /* Account for new node */ - if (i->parent) { - if (domain_entry_inc(conn, i)) { -- destroy_node_rm(i); -+ destroy_node_rm(conn, i); - return NULL; - } - } -@@ -2291,7 +2406,7 @@ static int clean_store_(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA val, - if (!hashtable_search(reachable, name)) { - log("clean_store: '%s' is orphaned!", name); - if (recovery) { -- tdb_delete(tdb, key); -+ do_tdb_delete(NULL, &key, NULL); - } - } - -@@ -3149,6 +3264,7 @@ void read_state_node(const void *ctx, const void *state) - if (!node) - barf("allocation error restoring node"); - -+ node->acc.memory = 0; - node->name = name; - node->generation = ++generation; - node->datalen = sn->data_len; -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index 373af18297bf..da9ecce67f31 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -176,6 +176,11 @@ struct node_perms { - struct xs_permissions *p; - }; - -+struct node_account_data { -+ unsigned int domid; -+ int memory; /* -1 if unknown */ -+}; -+ - struct node { - const char *name; - /* Key used to update TDB */ -@@ -198,6 +203,9 @@ struct node { - /* Children, each nul-terminated. */ - unsigned int childlen; - char *children; -+ -+ /* Allocation information for node currently in store. */ -+ struct node_account_data acc; - }; - - /* Return the only argument in the input. */ -@@ -306,6 +314,10 @@ extern xengnttab_handle **xgt_handle; - int remember_string(struct hashtable *hash, const char *str); - - void set_tdb_key(const char *name, TDB_DATA *key); -+int do_tdb_write(struct connection *conn, TDB_DATA *key, TDB_DATA *data, -+ struct node_account_data *acc, bool no_quota_check); -+int do_tdb_delete(struct connection *conn, TDB_DATA *key, -+ struct node_account_data *acc); - - void conn_free_buffered_data(struct connection *conn); - -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index 7bd41eb475e3..ace9a11d77bb 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -153,6 +153,9 @@ struct transaction - /* List of all transactions active on this connection. */ - struct list_head list; - -+ /* Connection this transaction is associated with. */ -+ struct connection *conn; -+ - /* Connection-local identifier for this transaction. */ - uint32_t id; - -@@ -286,6 +289,8 @@ int access_node(struct connection *conn, struct node *node, - - introduce = true; - i->ta_node = false; -+ /* acc.memory < 0 means "unknown, get size from TDB". */ -+ node->acc.memory = -1; - - /* - * Additional transaction-specific node for read type. We only -@@ -410,11 +415,11 @@ static int finalize_transaction(struct connection *conn, - goto err; - hdr = (void *)data.dptr; - hdr->generation = ++generation; -- ret = tdb_store(tdb_ctx, key, data, -- TDB_REPLACE); -+ ret = do_tdb_write(conn, &key, &data, NULL, -+ true); - talloc_free(data.dptr); - } else { -- ret = tdb_delete(tdb_ctx, key); -+ ret = do_tdb_delete(conn, &key, NULL); - } - if (ret) - goto err; -@@ -425,7 +430,7 @@ static int finalize_transaction(struct connection *conn, - } - } - -- if (i->ta_node && tdb_delete(tdb_ctx, ta_key)) -+ if (i->ta_node && do_tdb_delete(conn, &ta_key, NULL)) - goto err; - list_del(&i->list); - talloc_free(i); -@@ -453,7 +458,7 @@ static int destroy_transaction(void *_transaction) - i->node); - if (trans_name) { - set_tdb_key(trans_name, &key); -- tdb_delete(tdb_ctx, key); -+ do_tdb_delete(trans->conn, &key, NULL); - } - } - list_del(&i->list); -@@ -497,6 +502,7 @@ int do_transaction_start(struct connection *conn, struct buffered_data *in) - - INIT_LIST_HEAD(&trans->accessed); - INIT_LIST_HEAD(&trans->changed_domains); -+ trans->conn = conn; - trans->fail = false; - trans->generation = ++generation; - diff --git a/xsa326-4.16-xenstored-15.patch b/xsa326-4.16-xenstored-15.patch deleted file mode 100644 index 8a42a36..0000000 --- a/xsa326-4.16-xenstored-15.patch +++ /dev/null @@ -1,54 +0,0 @@ -From c55a1ea0a5ea7f6a3dc850cb015a49ba9ec571ab Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:10 +0200 -Subject: tools/xenstore: add exports for quota variables - -Some quota variables are not exported via header files. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Acked-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index da9ecce67f31..bfd3fc1e9df3 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -275,6 +275,11 @@ extern TDB_CONTEXT *tdb_ctx; - extern int dom0_domid; - extern int dom0_event; - extern int priv_domid; -+extern int quota_nb_watch_per_domain; -+extern int quota_max_transaction; -+extern int quota_max_entry_size; -+extern int quota_nb_perms_per_node; -+extern int quota_max_path_len; - extern int quota_nb_entry_per_domain; - extern int quota_req_outstanding; - extern int quota_trans_nodes; -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index ace9a11d77bb..28774813de83 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -175,7 +175,6 @@ struct transaction - bool fail; - }; - --extern int quota_max_transaction; - uint64_t generation; - - static struct accessed_node *find_accessed_node(struct transaction *trans, -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index fdf9b2d653a0..85362bcce314 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -31,8 +31,6 @@ - #include "xenstored_domain.h" - #include "xenstored_transaction.h" - --extern int quota_nb_watch_per_domain; -- - struct watch - { - /* Watches on this connection */ diff --git a/xsa326-4.16-xenstored-16.patch b/xsa326-4.16-xenstored-16.patch deleted file mode 100644 index c2a8953..0000000 --- a/xsa326-4.16-xenstored-16.patch +++ /dev/null @@ -1,238 +0,0 @@ -From 05cc2af50ba43431d6d50aff758e968833aab9c6 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:10 +0200 -Subject: tools/xenstore: add control command for setting and showing quota - -Add a xenstore-control command "quota" to: -- show current quota settings -- change quota settings -- show current quota related values of a domain - -Note that in the case the new quota is lower than existing one, -Xenstored may continue to handle requests from a domain exceeding the -new limit (depends on which one has been broken) and the amount of -resource used will not change. However the domain will not be able to -create more resource (associated to the quota) until it is back to below -the limit. - -This is part of XSA-326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/docs/misc/xenstore.txt b/docs/misc/xenstore.txt -index 334dc8b6fdf5..a7d006519ae8 100644 ---- a/docs/misc/xenstore.txt -+++ b/docs/misc/xenstore.txt -@@ -366,6 +366,17 @@ CONTROL |[|] - print| - print to syslog (xenstore runs as daemon) or - to console (xenstore runs as stubdom) -+ quota|[set |] -+ without parameters: print the current quota settings -+ with "set ": set the quota to new value -+ (The admin should make sure all the domain usage is -+ below the quota. If it is not, then Xenstored may continue to -+ handle requests from the domain as long as the resource -+ violating the new quota setting isn't increased further) -+ with "": print quota related accounting data for -+ the domain -+ quota-soft|[set ] -+ like the "quota" command, but for soft-quota. - help - return list of supported commands for CONTROL - -diff --git a/tools/xenstore/xenstored_control.c b/tools/xenstore/xenstored_control.c -index adb8d51b043b..1031a81c3874 100644 ---- a/tools/xenstore/xenstored_control.c -+++ b/tools/xenstore/xenstored_control.c -@@ -196,6 +196,115 @@ static int do_control_log(void *ctx, struct connection *conn, - return 0; - } - -+struct quota { -+ const char *name; -+ int *quota; -+ const char *descr; -+}; -+ -+static const struct quota hard_quotas[] = { -+ { "nodes", "a_nb_entry_per_domain, "Nodes per domain" }, -+ { "watches", "a_nb_watch_per_domain, "Watches per domain" }, -+ { "transactions", "a_max_transaction, "Transactions per domain" }, -+ { "outstanding", "a_req_outstanding, -+ "Outstanding requests per domain" }, -+ { "transaction-nodes", "a_trans_nodes, -+ "Max. number of accessed nodes per transaction" }, -+ { "memory", "a_memory_per_domain_hard, -+ "Total Xenstore memory per domain (error level)" }, -+ { "node-size", "a_max_entry_size, "Max. size of a node" }, -+ { "path-max", "a_max_path_len, "Max. length of a node path" }, -+ { "permissions", "a_nb_perms_per_node, -+ "Max. number of permissions per node" }, -+ { NULL, NULL, NULL } -+}; -+ -+static const struct quota soft_quotas[] = { -+ { "memory", "a_memory_per_domain_soft, -+ "Total Xenstore memory per domain (warning level)" }, -+ { NULL, NULL, NULL } -+}; -+ -+static int quota_show_current(const void *ctx, struct connection *conn, -+ const struct quota *quotas) -+{ -+ char *resp; -+ unsigned int i; -+ -+ resp = talloc_strdup(ctx, "Quota settings:\n"); -+ if (!resp) -+ return ENOMEM; -+ -+ for (i = 0; quotas[i].quota; i++) { -+ resp = talloc_asprintf_append(resp, "%-17s: %8d %s\n", -+ quotas[i].name, *quotas[i].quota, -+ quotas[i].descr); -+ if (!resp) -+ return ENOMEM; -+ } -+ -+ send_reply(conn, XS_CONTROL, resp, strlen(resp) + 1); -+ -+ return 0; -+} -+ -+static int quota_set(const void *ctx, struct connection *conn, -+ char **vec, int num, const struct quota *quotas) -+{ -+ unsigned int i; -+ int val; -+ -+ if (num != 2) -+ return EINVAL; -+ -+ val = atoi(vec[1]); -+ if (val < 1) -+ return EINVAL; -+ -+ for (i = 0; quotas[i].quota; i++) { -+ if (!strcmp(vec[0], quotas[i].name)) { -+ *quotas[i].quota = val; -+ send_ack(conn, XS_CONTROL); -+ return 0; -+ } -+ } -+ -+ return EINVAL; -+} -+ -+static int quota_get(const void *ctx, struct connection *conn, -+ char **vec, int num) -+{ -+ if (num != 1) -+ return EINVAL; -+ -+ return domain_get_quota(ctx, conn, atoi(vec[0])); -+} -+ -+static int do_control_quota(void *ctx, struct connection *conn, -+ char **vec, int num) -+{ -+ if (num == 0) -+ return quota_show_current(ctx, conn, hard_quotas); -+ -+ if (!strcmp(vec[0], "set")) -+ return quota_set(ctx, conn, vec + 1, num - 1, hard_quotas); -+ -+ return quota_get(ctx, conn, vec, num); -+} -+ -+static int do_control_quota_s(void *ctx, struct connection *conn, -+ char **vec, int num) -+{ -+ if (num == 0) -+ return quota_show_current(ctx, conn, soft_quotas); -+ -+ if (!strcmp(vec[0], "set")) -+ return quota_set(ctx, conn, vec + 1, num - 1, soft_quotas); -+ -+ return EINVAL; -+} -+ - #ifdef __MINIOS__ - static int do_control_memreport(void *ctx, struct connection *conn, - char **vec, int num) -@@ -847,6 +956,8 @@ static struct cmd_s cmds[] = { - { "memreport", do_control_memreport, "[]" }, - #endif - { "print", do_control_print, "" }, -+ { "quota", do_control_quota, "[set |]" }, -+ { "quota-soft", do_control_quota_s, "[set ]" }, - { "help", do_control_help, "" }, - }; - -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index 94fd561e9de4..e7c6886ccf47 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -31,6 +31,7 @@ - #include "xenstored_domain.h" - #include "xenstored_transaction.h" - #include "xenstored_watch.h" -+#include "xenstored_control.h" - - #include - #include -@@ -345,6 +346,38 @@ static struct domain *find_domain_struct(unsigned int domid) - return NULL; - } - -+int domain_get_quota(const void *ctx, struct connection *conn, -+ unsigned int domid) -+{ -+ struct domain *d = find_domain_struct(domid); -+ char *resp; -+ int ta; -+ -+ if (!d) -+ return ENOENT; -+ -+ ta = d->conn ? d->conn->transaction_started : 0; -+ resp = talloc_asprintf(ctx, "Domain %u:\n", domid); -+ if (!resp) -+ return ENOMEM; -+ -+#define ent(t, e) \ -+ resp = talloc_asprintf_append(resp, "%-16s: %8d\n", #t, e); \ -+ if (!resp) return ENOMEM -+ -+ ent(nodes, d->nbentry); -+ ent(watches, d->nbwatch); -+ ent(transactions, ta); -+ ent(outstanding, d->nboutstanding); -+ ent(memory, d->memory); -+ -+#undef ent -+ -+ send_reply(conn, XS_CONTROL, resp, strlen(resp) + 1); -+ -+ return 0; -+} -+ - static struct domain *alloc_domain(const void *context, unsigned int domid) - { - struct domain *domain; -diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h -index 633c9a0a0a1f..904faa923afb 100644 ---- a/tools/xenstore/xenstored_domain.h -+++ b/tools/xenstore/xenstored_domain.h -@@ -87,6 +87,8 @@ int domain_watch(struct connection *conn); - void domain_outstanding_inc(struct connection *conn); - void domain_outstanding_dec(struct connection *conn); - void domain_outstanding_domid_dec(unsigned int domid); -+int domain_get_quota(const void *ctx, struct connection *conn, -+ unsigned int domid); - - /* Special node permission handling. */ - int set_perms_special(struct connection *conn, const char *name, diff --git a/xsa409-4.13-0001-libxl-docs-Use-arch-specific-default-paging-memory.patch b/xsa409-4.13-0001-libxl-docs-Use-arch-specific-default-paging-memory.patch deleted file mode 100644 index 9aca0a7..0000000 --- a/xsa409-4.13-0001-libxl-docs-Use-arch-specific-default-paging-memory.patch +++ /dev/null @@ -1,147 +0,0 @@ -From bd4a7db4001364fd03a80a2e73b81c46aaa44e9c Mon Sep 17 00:00:00 2001 -From: Henry Wang -Date: Mon, 22 Aug 2022 01:35:09 +0000 -Subject: [PATCH 1/4] libxl, docs: Use arch-specific default paging memory - -The default paging memory (descibed in `shadow_memory` entry in xl -config) in libxl is used to determine the memory pool size for xl -guests. Currently this size is only used for x86, and contains a part -of RAM to shadow the resident processes. Since on Arm there is no -shadow mode guests, so the part of RAM to shadow the resident processes -is not necessary. Therefore, this commit splits the function -`libxl_get_required_shadow_memory()` to arch specific helpers and -renamed the helper to `libxl__arch_get_required_paging_memory()`. - -On x86, this helper calls the original value from -`libxl_get_required_shadow_memory()` so no functional change intended. - -On Arm, this helper returns 1MB per vcpu plus 4KB per MiB of RAM -for the P2M map and additional 512KB. - -Also update the xl.cfg documentation to add Arm documentation -according to code changes and correct the comment style following Xen -coding style. - -This is part of CVE-2022-33747 / XSA-409. - -Suggested-by: Julien Grall -Signed-off-by: Henry Wang -Reviewed-by: Anthony PERARD ---- - docs/man/xl.cfg.5.pod.in | 5 +++++ - tools/libs/light/libxl_arch.h | 4 ++++ - tools/libs/light/libxl_arm.c | 14 ++++++++++++++ - tools/libs/light/libxl_utils.c | 9 ++------- - tools/libs/light/libxl_x86.c | 13 +++++++++++++ - 5 files changed, 38 insertions(+), 7 deletions(-) - -diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in -index b98d1613987e..eda1e77ebd06 100644 ---- a/docs/man/xl.cfg.5.pod.in -+++ b/docs/man/xl.cfg.5.pod.in -@@ -1768,6 +1768,11 @@ are not using hardware assisted paging (i.e. you are using shadow - mode) and your guest workload consists of a very large number of - similar processes then increasing this value may improve performance. - -+On Arm, this field is used to determine the size of the guest P2M pages -+pool, and the default value is 1MB per vCPU plus 4KB per MB of RAM for -+the P2M map and additional 512KB for extended regions. Users should -+adjust this value if bigger P2M pool size is needed. -+ - =back - - =head3 Processor and Platform Features -diff --git a/tools/libs/light/libxl_arch.h b/tools/libs/light/libxl_arch.h -index 1522ecb97f72..5a060c2c3033 100644 ---- a/tools/libs/light/libxl_arch.h -+++ b/tools/libs/light/libxl_arch.h -@@ -90,6 +90,10 @@ void libxl__arch_update_domain_config(libxl__gc *gc, - libxl_domain_config *dst, - const libxl_domain_config *src); - -+_hidden -+unsigned long libxl__arch_get_required_paging_memory(unsigned long maxmem_kb, -+ unsigned int smp_cpus); -+ - #if defined(__i386__) || defined(__x86_64__) - - #define LAPIC_BASE_ADDRESS 0xfee00000 -diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c -index eef1de093914..73a95e83af24 100644 ---- a/tools/libs/light/libxl_arm.c -+++ b/tools/libs/light/libxl_arm.c -@@ -154,6 +154,20 @@ out: - return rc; - } - -+unsigned long libxl__arch_get_required_paging_memory(unsigned long maxmem_kb, -+ unsigned int smp_cpus) -+{ -+ /* -+ * 256 pages (1MB) per vcpu, -+ * plus 1 page per MiB of RAM for the P2M map, -+ * plus 1 page per MiB of extended region. This default value is 128 MiB -+ * which should be enough for domains that are not running backend. -+ * This is higher than the minimum that Xen would allocate if no value -+ * were given (but the Xen minimum is for safety, not performance). -+ */ -+ return 4 * (256 * smp_cpus + maxmem_kb / 1024 + 128); -+} -+ - static struct arch_info { - const char *guest_type; - const char *timer_compat; -diff --git a/tools/libs/light/libxl_utils.c b/tools/libs/light/libxl_utils.c -index 4699c4a0a36f..e276c0ee9cc3 100644 ---- a/tools/libs/light/libxl_utils.c -+++ b/tools/libs/light/libxl_utils.c -@@ -18,6 +18,7 @@ - #include - - #include "libxl_internal.h" -+#include "libxl_arch.h" - #include "_paths.h" - - #ifndef LIBXL_HAVE_NONCONST_LIBXL_BASENAME_RETURN_VALUE -@@ -39,13 +40,7 @@ char *libxl_basename(const char *name) - - unsigned long libxl_get_required_shadow_memory(unsigned long maxmem_kb, unsigned int smp_cpus) - { -- /* 256 pages (1MB) per vcpu, -- plus 1 page per MiB of RAM for the P2M map, -- plus 1 page per MiB of RAM to shadow the resident processes. -- This is higher than the minimum that Xen would allocate if no value -- were given (but the Xen minimum is for safety, not performance). -- */ -- return 4 * (256 * smp_cpus + 2 * (maxmem_kb / 1024)); -+ return libxl__arch_get_required_paging_memory(maxmem_kb, smp_cpus); - } - - char *libxl_domid_to_name(libxl_ctx *ctx, uint32_t domid) -diff --git a/tools/libs/light/libxl_x86.c b/tools/libs/light/libxl_x86.c -index 1feadebb1852..51362893cf98 100644 ---- a/tools/libs/light/libxl_x86.c -+++ b/tools/libs/light/libxl_x86.c -@@ -882,6 +882,19 @@ void libxl__arch_update_domain_config(libxl__gc *gc, - libxl_defbool_val(src->b_info.arch_x86.msr_relaxed)); - } - -+unsigned long libxl__arch_get_required_paging_memory(unsigned long maxmem_kb, -+ unsigned int smp_cpus) -+{ -+ /* -+ * 256 pages (1MB) per vcpu, -+ * plus 1 page per MiB of RAM for the P2M map, -+ * plus 1 page per MiB of RAM to shadow the resident processes. -+ * This is higher than the minimum that Xen would allocate if no value -+ * were given (but the Xen minimum is for safety, not performance). -+ */ -+ return 4 * (256 * smp_cpus + 2 * (maxmem_kb / 1024)); -+} -+ - /* - * Local variables: - * mode: C --- -2.37.1 - diff --git a/xsa409-4.13-0002-xen-arm-Construct-the-P2M-pages-pool-for-guests.patch b/xsa409-4.13-0002-xen-arm-Construct-the-P2M-pages-pool-for-guests.patch deleted file mode 100644 index 8c03657..0000000 --- a/xsa409-4.13-0002-xen-arm-Construct-the-P2M-pages-pool-for-guests.patch +++ /dev/null @@ -1,187 +0,0 @@ -From 419a4bbc20cf7c5d7d9dedae59fb8049922e6a2c Mon Sep 17 00:00:00 2001 -From: Henry Wang -Date: Mon, 6 Jun 2022 06:17:28 +0000 -Subject: [PATCH 2/4] xen/arm: Construct the P2M pages pool for guests - -This commit constructs the p2m pages pool for guests from the -data structure and helper perspective. - -This is implemented by: - -- Adding a `struct paging_domain` which contains a freelist, a -counter variable and a spinlock to `struct arch_domain` to -indicate the free p2m pages and the number of p2m total pages in -the p2m pages pool. - -- Adding a helper `p2m_get_allocation` to get the p2m pool size. - -- Adding a helper `p2m_set_allocation` to set the p2m pages pool -size. This helper should be called before allocating memory for -a guest. - -- Adding a helper `p2m_teardown_allocation` to free the p2m pages -pool. This helper should be called during the xl domain destory. - -This is part of CVE-2022-33747 / XSA-409. - -Signed-off-by: Henry Wang -Reviewed-by: Stefano Stabellini ---- - xen/arch/arm/p2m.c | 88 ++++++++++++++++++++++++++++++++++++ - xen/include/asm-arm/domain.h | 10 ++++ - xen/include/asm-arm/p2m.h | 4 ++ - 3 files changed, 102 insertions(+) - -diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c -index 3bcd1e897e88..79f3d37f5230 100644 ---- a/xen/arch/arm/p2m.c -+++ b/xen/arch/arm/p2m.c -@@ -50,6 +50,92 @@ static uint64_t generate_vttbr(uint16_t vmid, mfn_t root_mfn) - return (mfn_to_maddr(root_mfn) | ((uint64_t)vmid << 48)); - } - -+/* Return the size of the pool, rounded up to the nearest MB */ -+unsigned int p2m_get_allocation(struct domain *d) -+{ -+ unsigned long nr_pages = ACCESS_ONCE(d->arch.paging.p2m_total_pages); -+ -+ return ROUNDUP(nr_pages, 1 << (20 - PAGE_SHIFT)) >> (20 - PAGE_SHIFT); -+} -+ -+/* -+ * Set the pool of pages to the required number of pages. -+ * Returns 0 for success, non-zero for failure. -+ * Call with d->arch.paging.lock held. -+ */ -+int p2m_set_allocation(struct domain *d, unsigned long pages, bool *preempted) -+{ -+ struct page_info *pg; -+ -+ ASSERT(spin_is_locked(&d->arch.paging.lock)); -+ -+ for ( ; ; ) -+ { -+ if ( d->arch.paging.p2m_total_pages < pages ) -+ { -+ /* Need to allocate more memory from domheap */ -+ pg = alloc_domheap_page(NULL, 0); -+ if ( pg == NULL ) -+ { -+ printk(XENLOG_ERR "Failed to allocate P2M pages.\n"); -+ return -ENOMEM; -+ } -+ ACCESS_ONCE(d->arch.paging.p2m_total_pages) = -+ d->arch.paging.p2m_total_pages + 1; -+ page_list_add_tail(pg, &d->arch.paging.p2m_freelist); -+ } -+ else if ( d->arch.paging.p2m_total_pages > pages ) -+ { -+ /* Need to return memory to domheap */ -+ pg = page_list_remove_head(&d->arch.paging.p2m_freelist); -+ if( pg ) -+ { -+ ACCESS_ONCE(d->arch.paging.p2m_total_pages) = -+ d->arch.paging.p2m_total_pages - 1; -+ free_domheap_page(pg); -+ } -+ else -+ { -+ printk(XENLOG_ERR -+ "Failed to free P2M pages, P2M freelist is empty.\n"); -+ return -ENOMEM; -+ } -+ } -+ else -+ break; -+ -+ /* Check to see if we need to yield and try again */ -+ if ( preempted && general_preempt_check() ) -+ { -+ *preempted = true; -+ return -ERESTART; -+ } -+ } -+ -+ return 0; -+} -+ -+int p2m_teardown_allocation(struct domain *d) -+{ -+ int ret = 0; -+ bool preempted = false; -+ -+ spin_lock(&d->arch.paging.lock); -+ if ( d->arch.paging.p2m_total_pages != 0 ) -+ { -+ ret = p2m_set_allocation(d, 0, &preempted); -+ if ( preempted ) -+ { -+ spin_unlock(&d->arch.paging.lock); -+ return -ERESTART; -+ } -+ ASSERT(d->arch.paging.p2m_total_pages == 0); -+ } -+ spin_unlock(&d->arch.paging.lock); -+ -+ return ret; -+} -+ - /* Unlock the flush and do a P2M TLB flush if necessary */ - void p2m_write_unlock(struct p2m_domain *p2m) - { -@@ -1599,7 +1685,9 @@ int p2m_init(struct domain *d) - unsigned int cpu; - - rwlock_init(&p2m->lock); -+ spin_lock_init(&d->arch.paging.lock); - INIT_PAGE_LIST_HEAD(&p2m->pages); -+ INIT_PAGE_LIST_HEAD(&d->arch.paging.p2m_freelist); - - p2m->vmid = INVALID_VMID; - -diff --git a/xen/include/asm-arm/domain.h b/xen/include/asm-arm/domain.h -index 9b3647587a04..c90daa65afa7 100644 ---- a/xen/include/asm-arm/domain.h -+++ b/xen/include/asm-arm/domain.h -@@ -40,6 +40,14 @@ struct vtimer { - uint64_t cval; - }; - -+struct paging_domain { -+ spinlock_t lock; -+ /* Free P2M pages from the pre-allocated P2M pool */ -+ struct page_list_head p2m_freelist; -+ /* Number of pages from the pre-allocated P2M pool */ -+ unsigned long p2m_total_pages; -+}; -+ - struct arch_domain - { - #ifdef CONFIG_ARM_64 -@@ -51,6 +59,8 @@ struct arch_domain - - struct hvm_domain hvm; - -+ struct paging_domain paging; -+ - struct vmmio vmmio; - - /* Continuable domain_relinquish_resources(). */ -diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h -index b3ba83283e11..c9598740bd02 100644 ---- a/xen/include/asm-arm/p2m.h -+++ b/xen/include/asm-arm/p2m.h -@@ -218,6 +218,10 @@ void p2m_restore_state(struct vcpu *n); - /* Print debugging/statistial info about a domain's p2m */ - void p2m_dump_info(struct domain *d); - -+unsigned int p2m_get_allocation(struct domain *d); -+int p2m_set_allocation(struct domain *d, unsigned long pages, bool *preempted); -+int p2m_teardown_allocation(struct domain *d); -+ - static inline void p2m_write_lock(struct p2m_domain *p2m) - { - write_lock(&p2m->lock); --- -2.37.1 - diff --git a/xsa409-4.13-0003-xen-arm-libxl-Implement-XEN_DOMCTL_shadow_op-for-Arm.patch b/xsa409-4.13-0003-xen-arm-libxl-Implement-XEN_DOMCTL_shadow_op-for-Arm.patch deleted file mode 100644 index bb638e1..0000000 --- a/xsa409-4.13-0003-xen-arm-libxl-Implement-XEN_DOMCTL_shadow_op-for-Arm.patch +++ /dev/null @@ -1,106 +0,0 @@ -From 332a9979d4dd0b047aa16db201c50fcedbd56743 Mon Sep 17 00:00:00 2001 -From: Henry Wang -Date: Mon, 6 Jun 2022 06:17:29 +0000 -Subject: [PATCH 3/4] xen/arm, libxl: Implement XEN_DOMCTL_shadow_op for Arm - -This commit implements the `XEN_DOMCTL_shadow_op` support in Xen -for Arm. The p2m pages pool size for xl guests is supposed to be -determined by `XEN_DOMCTL_shadow_op`. Hence, this commit: - -- Introduces a function `p2m_domctl` and implements the subops -`XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION` and -`XEN_DOMCTL_SHADOW_OP_GET_ALLOCATION` of `XEN_DOMCTL_shadow_op`. - -- Adds the `XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION` support in libxl. - -Therefore enabling the setting of shadow memory pool size -when creating a guest from xl and getting shadow memory pool size -from Xen. - -Note that the `XEN_DOMCTL_shadow_op` added in this commit is only -a dummy op, and the functionality of setting/getting p2m memory pool -size for xl guests will be added in following commits. - -This is part of CVE-2022-33747 / XSA-409. - -Signed-off-by: Henry Wang -Reviewed-by: Stefano Stabellini ---- - tools/libs/light/libxl_arm.c | 12 ++++++++++++ - xen/arch/arm/domctl.c | 32 ++++++++++++++++++++++++++++++++ - 2 files changed, 44 insertions(+) - -diff --git a/tools/libs/light/libxl_arm.c b/tools/libs/light/libxl_arm.c -index 73a95e83af24..22a0c561bbc6 100644 ---- a/tools/libs/light/libxl_arm.c -+++ b/tools/libs/light/libxl_arm.c -@@ -131,6 +131,18 @@ int libxl__arch_domain_create(libxl__gc *gc, - libxl__domain_build_state *state, - uint32_t domid) - { -+ libxl_ctx *ctx = libxl__gc_owner(gc); -+ unsigned int shadow_mb = DIV_ROUNDUP(d_config->b_info.shadow_memkb, 1024); -+ -+ int r = xc_shadow_control(ctx->xch, domid, -+ XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION, -+ &shadow_mb, 0); -+ if (r) { -+ LOGED(ERROR, domid, -+ "Failed to set %u MiB shadow allocation", shadow_mb); -+ return ERROR_FAIL; -+ } -+ - return 0; - } - -diff --git a/xen/arch/arm/domctl.c b/xen/arch/arm/domctl.c -index 1baf25c3d98b..9bf72e693019 100644 ---- a/xen/arch/arm/domctl.c -+++ b/xen/arch/arm/domctl.c -@@ -47,11 +47,43 @@ static int handle_vuart_init(struct domain *d, - return rc; - } - -+static long p2m_domctl(struct domain *d, struct xen_domctl_shadow_op *sc, -+ XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) -+{ -+ if ( unlikely(d == current->domain) ) -+ { -+ printk(XENLOG_ERR "Tried to do a p2m domctl op on itself.\n"); -+ return -EINVAL; -+ } -+ -+ if ( unlikely(d->is_dying) ) -+ { -+ printk(XENLOG_ERR "Tried to do a p2m domctl op on dying domain %u\n", -+ d->domain_id); -+ return -EINVAL; -+ } -+ -+ switch ( sc->op ) -+ { -+ case XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION: -+ return 0; -+ case XEN_DOMCTL_SHADOW_OP_GET_ALLOCATION: -+ return 0; -+ default: -+ { -+ printk(XENLOG_ERR "Bad p2m domctl op %u\n", sc->op); -+ return -EINVAL; -+ } -+ } -+} -+ - long arch_do_domctl(struct xen_domctl *domctl, struct domain *d, - XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) - { - switch ( domctl->cmd ) - { -+ case XEN_DOMCTL_shadow_op: -+ return p2m_domctl(d, &domctl->u.shadow_op, u_domctl); - case XEN_DOMCTL_cacheflush: - { - gfn_t s = _gfn(domctl->u.cacheflush.start_pfn); --- -2.37.1 - diff --git a/xsa409-4.13-0004-xen-arm-Allocate-and-free-P2M-pages-from-the-P2M-poo.patch b/xsa409-4.13-0004-xen-arm-Allocate-and-free-P2M-pages-from-the-P2M-poo.patch deleted file mode 100644 index 5366c8f..0000000 --- a/xsa409-4.13-0004-xen-arm-Allocate-and-free-P2M-pages-from-the-P2M-poo.patch +++ /dev/null @@ -1,287 +0,0 @@ -From 39664d9ee041f96e9c7ee131ed8ef72a4d19c9f8 Mon Sep 17 00:00:00 2001 -From: Henry Wang -Date: Mon, 6 Jun 2022 06:17:30 +0000 -Subject: [PATCH 4/4] xen/arm: Allocate and free P2M pages from the P2M pool - -This commit sets/tearsdown of p2m pages pool for non-privileged Arm -guests by calling `p2m_set_allocation` and `p2m_teardown_allocation`. - -- For dom0, P2M pages should come from heap directly instead of p2m -pool, so that the kernel may take advantage of the extended regions. - -- For xl guests, the setting of the p2m pool is called in -`XEN_DOMCTL_shadow_op` and the p2m pool is destroyed in -`domain_relinquish_resources`. Note that domctl->u.shadow_op.mb is -updated with the new size when setting the p2m pool. - -- For dom0less domUs, the setting of the p2m pool is called before -allocating memory during domain creation. Users can specify the p2m -pool size by `xen,domain-p2m-mem-mb` dts property. - -To actually allocate/free pages from the p2m pool, this commit adds -two helper functions namely `p2m_alloc_page` and `p2m_free_page` to -`struct p2m_domain`. By replacing the `alloc_domheap_page` and -`free_domheap_page` with these two helper functions, p2m pages can -be added/removed from the list of p2m pool rather than from the heap. - -Since page from `p2m_alloc_page` is cleaned, take the opportunity -to remove the redundant `clean_page` in `p2m_create_table`. - -This is part of CVE-2022-33747 / XSA-409. - -Signed-off-by: Henry Wang -Reviewed-by: Stefano Stabellini ---- - docs/misc/arm/device-tree/booting.txt | 8 ++++ - xen/arch/arm/domain.c | 6 +++ - xen/arch/arm/domain_build.c | 29 ++++++++++++++ - xen/arch/arm/domctl.c | 23 ++++++++++- - xen/arch/arm/p2m.c | 57 +++++++++++++++++++++++++-- - 5 files changed, 118 insertions(+), 5 deletions(-) - -diff --git a/docs/misc/arm/device-tree/booting.txt b/docs/misc/arm/device-tree/booting.txt -index 71895663a4de..d92ccc56ffe0 100644 ---- a/docs/misc/arm/device-tree/booting.txt -+++ b/docs/misc/arm/device-tree/booting.txt -@@ -182,6 +182,14 @@ with the following properties: - Both #address-cells and #size-cells need to be specified because - both sub-nodes (described shortly) have reg properties. - -+- xen,domain-p2m-mem-mb -+ -+ Optional. A 32-bit integer specifying the amount of megabytes of RAM -+ used for the domain P2M pool. This is in-sync with the shadow_memory -+ option in xl.cfg. Leaving this field empty in device tree will lead to -+ the default size of domain P2M pool, i.e. 1MB per guest vCPU plus 4KB -+ per MB of guest RAM plus 512KB for guest extended regions. -+ - Under the "xen,domain" compatible node, one or more sub-nodes are present - for the DomU kernel and ramdisk. - -diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c -index 2694c39127c5..a818f33a1afa 100644 ---- a/xen/arch/arm/domain.c -+++ b/xen/arch/arm/domain.c -@@ -997,6 +997,7 @@ enum { - PROG_page, - PROG_mapping, - PROG_p2m, -+ PROG_p2m_pool, - PROG_done, - }; - -@@ -1062,6 +1063,11 @@ int domain_relinquish_resources(struct domain *d) - if ( ret ) - return ret; - -+ PROGRESS(p2m_pool): -+ ret = p2m_teardown_allocation(d); -+ if( ret ) -+ return ret; -+ - PROGRESS(done): - break; - -diff --git a/xen/arch/arm/domain_build.c b/xen/arch/arm/domain_build.c -index d02bacbcd1ed..8aec3755ca5d 100644 ---- a/xen/arch/arm/domain_build.c -+++ b/xen/arch/arm/domain_build.c -@@ -2833,6 +2833,21 @@ static void __init find_gnttab_region(struct domain *d, - kinfo->gnttab_start, kinfo->gnttab_start + kinfo->gnttab_size); - } - -+static unsigned long __init domain_p2m_pages(unsigned long maxmem_kb, -+ unsigned int smp_cpus) -+{ -+ /* -+ * Keep in sync with libxl__get_required_paging_memory(). -+ * 256 pages (1MB) per vcpu, plus 1 page per MiB of RAM for the P2M map, -+ * plus 128 pages to cover extended regions. -+ */ -+ unsigned long memkb = 4 * (256 * smp_cpus + (maxmem_kb / 1024) + 128); -+ -+ BUILD_BUG_ON(PAGE_SIZE != SZ_4K); -+ -+ return DIV_ROUND_UP(memkb, 1024) << (20 - PAGE_SHIFT); -+} -+ - static int __init construct_domain(struct domain *d, struct kernel_info *kinfo) - { - unsigned int i; -@@ -2924,6 +2939,8 @@ static int __init construct_domU(struct domain *d, - struct kernel_info kinfo = {}; - int rc; - u64 mem; -+ u32 p2m_mem_mb; -+ unsigned long p2m_pages; - - rc = dt_property_read_u64(node, "memory", &mem); - if ( !rc ) -@@ -2933,6 +2950,18 @@ static int __init construct_domU(struct domain *d, - } - kinfo.unassigned_mem = (paddr_t)mem * SZ_1K; - -+ rc = dt_property_read_u32(node, "xen,domain-p2m-mem-mb", &p2m_mem_mb); -+ /* If xen,domain-p2m-mem-mb is not specified, use the default value. */ -+ p2m_pages = rc ? -+ p2m_mem_mb << (20 - PAGE_SHIFT) : -+ domain_p2m_pages(mem, d->max_vcpus); -+ -+ spin_lock(&d->arch.paging.lock); -+ rc = p2m_set_allocation(d, p2m_pages, NULL); -+ spin_unlock(&d->arch.paging.lock); -+ if ( rc != 0 ) -+ return rc; -+ - printk("*** LOADING DOMU cpus=%u memory=%"PRIx64"KB ***\n", d->max_vcpus, mem); - - kinfo.vpl011 = dt_property_read_bool(node, "vpl011"); -diff --git a/xen/arch/arm/domctl.c b/xen/arch/arm/domctl.c -index 9bf72e693019..c8fdeb124084 100644 ---- a/xen/arch/arm/domctl.c -+++ b/xen/arch/arm/domctl.c -@@ -50,6 +50,9 @@ static int handle_vuart_init(struct domain *d, - static long p2m_domctl(struct domain *d, struct xen_domctl_shadow_op *sc, - XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) - { -+ long rc; -+ bool preempted = false; -+ - if ( unlikely(d == current->domain) ) - { - printk(XENLOG_ERR "Tried to do a p2m domctl op on itself.\n"); -@@ -66,9 +69,27 @@ static long p2m_domctl(struct domain *d, struct xen_domctl_shadow_op *sc, - switch ( sc->op ) - { - case XEN_DOMCTL_SHADOW_OP_SET_ALLOCATION: -- return 0; -+ { -+ /* Allow and handle preemption */ -+ spin_lock(&d->arch.paging.lock); -+ rc = p2m_set_allocation(d, sc->mb << (20 - PAGE_SHIFT), &preempted); -+ spin_unlock(&d->arch.paging.lock); -+ -+ if ( preempted ) -+ /* Not finished. Set up to re-run the call. */ -+ rc = hypercall_create_continuation(__HYPERVISOR_domctl, "h", -+ u_domctl); -+ else -+ /* Finished. Return the new allocation. */ -+ sc->mb = p2m_get_allocation(d); -+ -+ return rc; -+ } - case XEN_DOMCTL_SHADOW_OP_GET_ALLOCATION: -+ { -+ sc->mb = p2m_get_allocation(d); - return 0; -+ } - default: - { - printk(XENLOG_ERR "Bad p2m domctl op %u\n", sc->op); -diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c -index 79f3d37f5230..1bf9cbeb53cf 100644 ---- a/xen/arch/arm/p2m.c -+++ b/xen/arch/arm/p2m.c -@@ -50,6 +50,54 @@ static uint64_t generate_vttbr(uint16_t vmid, mfn_t root_mfn) - return (mfn_to_maddr(root_mfn) | ((uint64_t)vmid << 48)); - } - -+static struct page_info *p2m_alloc_page(struct domain *d) -+{ -+ struct page_info *pg; -+ -+ spin_lock(&d->arch.paging.lock); -+ /* -+ * For hardware domain, there should be no limit in the number of pages that -+ * can be allocated, so that the kernel may take advantage of the extended -+ * regions. Hence, allocate p2m pages for hardware domains from heap. -+ */ -+ if ( is_hardware_domain(d) ) -+ { -+ pg = alloc_domheap_page(NULL, 0); -+ if ( pg == NULL ) -+ { -+ printk(XENLOG_G_ERR "Failed to allocate P2M pages for hwdom.\n"); -+ spin_unlock(&d->arch.paging.lock); -+ return NULL; -+ } -+ } -+ else -+ { -+ pg = page_list_remove_head(&d->arch.paging.p2m_freelist); -+ if ( unlikely(!pg) ) -+ { -+ spin_unlock(&d->arch.paging.lock); -+ return NULL; -+ } -+ d->arch.paging.p2m_total_pages--; -+ } -+ spin_unlock(&d->arch.paging.lock); -+ -+ return pg; -+} -+ -+static void p2m_free_page(struct domain *d, struct page_info *pg) -+{ -+ spin_lock(&d->arch.paging.lock); -+ if ( is_hardware_domain(d) ) -+ free_domheap_page(pg); -+ else -+ { -+ d->arch.paging.p2m_total_pages++; -+ page_list_add_tail(pg, &d->arch.paging.p2m_freelist); -+ } -+ spin_unlock(&d->arch.paging.lock); -+} -+ - /* Return the size of the pool, rounded up to the nearest MB */ - unsigned int p2m_get_allocation(struct domain *d) - { -@@ -751,7 +799,7 @@ static int p2m_create_table(struct p2m_domain *p2m, lpae_t *entry) - - ASSERT(!p2m_is_valid(*entry)); - -- page = alloc_domheap_page(NULL, 0); -+ page = p2m_alloc_page(p2m->domain); - if ( page == NULL ) - return -ENOMEM; - -@@ -878,7 +926,7 @@ static void p2m_free_entry(struct p2m_domain *p2m, - pg = mfn_to_page(mfn); - - page_list_del(pg, &p2m->pages); -- free_domheap_page(pg); -+ p2m_free_page(p2m->domain, pg); - } - - static bool p2m_split_superpage(struct p2m_domain *p2m, lpae_t *entry, -@@ -902,7 +950,7 @@ static bool p2m_split_superpage(struct p2m_domain *p2m, lpae_t *entry, - ASSERT(level < target); - ASSERT(p2m_is_superpage(*entry, level)); - -- page = alloc_domheap_page(NULL, 0); -+ page = p2m_alloc_page(p2m->domain); - if ( !page ) - return false; - -@@ -1641,7 +1689,7 @@ int p2m_teardown(struct domain *d) - - while ( (pg = page_list_remove_head(&p2m->pages)) ) - { -- free_domheap_page(pg); -+ p2m_free_page(p2m->domain, pg); - count++; - /* Arbitrarily preempt every 512 iterations */ - if ( !(count % 512) && hypercall_preempt_check() ) -@@ -1665,6 +1713,7 @@ void p2m_final_teardown(struct domain *d) - return; - - ASSERT(page_list_empty(&p2m->pages)); -+ ASSERT(page_list_empty(&d->arch.paging.p2m_freelist)); - - if ( p2m->root ) - free_domheap_pages(p2m->root, P2M_ROOT_ORDER); --- -2.37.1 - diff --git a/xsa410-4.16-01.patch b/xsa410-4.16-01.patch deleted file mode 100644 index 8f352ee..0000000 --- a/xsa410-4.16-01.patch +++ /dev/null @@ -1,59 +0,0 @@ -From 4b4359122a414cc15156e13e3805988b71ff9da0 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 6 Jun 2022 06:17:25 +0000 -Subject: [PATCH 1/2] xen/arm: p2m: Prevent adding mapping when domain is dying - -During the domain destroy process, the domain will still be accessible -until it is fully destroyed. So does the P2M because we don't bail -out early if is_dying is non-zero. If a domain has permission to -modify the other domain's P2M (i.e. dom0, or a stubdomain), then -foreign mapping can be added past relinquish_p2m_mapping(). - -Therefore, we need to prevent mapping to be added when the domain -is dying. This commit prevents such adding of mapping by adding the -d->is_dying check to p2m_set_entry(). Also this commit enhances the -check in relinquish_p2m_mapping() to make sure that no mappings can -be added in the P2M after the P2M lock is released. - -This is part of CVE-2022-33746 / XSA-410. - -Signed-off-by: Julien Grall -Signed-off-by: Henry Wang -Tested-by: Henry Wang -Reviewed-by: Stefano Stabellini ---- - xen/arch/arm/p2m.c | 11 +++++++++++ - 1 file changed, 11 insertions(+) - -diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c -index fb71fa4c1c90..cbeff90f4371 100644 ---- a/xen/arch/arm/p2m.c -+++ b/xen/arch/arm/p2m.c -@@ -1093,6 +1093,15 @@ int p2m_set_entry(struct p2m_domain *p2m, - { - int rc = 0; - -+ /* -+ * Any reference taken by the P2M mappings (e.g. foreign mapping) will -+ * be dropped in relinquish_p2m_mapping(). As the P2M will still -+ * be accessible after, we need to prevent mapping to be added when the -+ * domain is dying. -+ */ -+ if ( unlikely(p2m->domain->is_dying) ) -+ return -ENOMEM; -+ - while ( nr ) - { - unsigned long mask; -@@ -1610,6 +1619,8 @@ int relinquish_p2m_mapping(struct domain *d) - unsigned int order; - gfn_t start, end; - -+ BUG_ON(!d->is_dying); -+ /* No mappings can be added in the P2M after the P2M lock is released. */ - p2m_write_lock(p2m); - - start = p2m->lowest_mapped_gfn; --- -2.37.1 - diff --git a/xsa410-4.16-02.patch b/xsa410-4.16-02.patch deleted file mode 100644 index 7599a47..0000000 --- a/xsa410-4.16-02.patch +++ /dev/null @@ -1,165 +0,0 @@ -From 0d5846490348fa09a0d0915d7c795685a016ce10 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Mon, 6 Jun 2022 06:17:26 +0000 -Subject: [PATCH 2/2] xen/arm: p2m: Handle preemption when freeing intermediate - page tables - -At the moment the P2M page tables will be freed when the domain structure -is freed without any preemption. As the P2M is quite large, iterating -through this may take more time than it is reasonable without intermediate -preemption (to run softirqs and perhaps scheduler). - -Split p2m_teardown() in two parts: one preemptible and called when -relinquishing the resources, the other one non-preemptible and called -when freeing the domain structure. - -As we are now freeing the P2M pages early, we also need to prevent -further allocation if someone call p2m_set_entry() past p2m_teardown() -(I wasn't able to prove this will never happen). This is done by -the checking domain->is_dying from previous patch in p2m_set_entry(). - -Similarly, we want to make sure that no-one can accessed the free -pages. Therefore the root is cleared before freeing pages. - -This is part of CVE-2022-33746 / XSA-410. - -Signed-off-by: Julien Grall -Signed-off-by: Henry Wang -Tested-by: Henry Wang -Reviewed-by: Stefano Stabellini ---- - xen/arch/arm/domain.c | 10 +++++++-- - xen/arch/arm/p2m.c | 47 ++++++++++++++++++++++++++++++++++++--- - xen/include/asm-arm/p2m.h | 13 +++++++++-- - 3 files changed, 63 insertions(+), 7 deletions(-) - -diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c -index 96e1b235501d..2694c39127c5 100644 ---- a/xen/arch/arm/domain.c -+++ b/xen/arch/arm/domain.c -@@ -789,10 +789,10 @@ fail: - void arch_domain_destroy(struct domain *d) - { - /* IOMMU page table is shared with P2M, always call -- * iommu_domain_destroy() before p2m_teardown(). -+ * iommu_domain_destroy() before p2m_final_teardown(). - */ - iommu_domain_destroy(d); -- p2m_teardown(d); -+ p2m_final_teardown(d); - domain_vgic_free(d); - domain_vuart_free(d); - free_xenheap_page(d->shared_info); -@@ -996,6 +996,7 @@ enum { - PROG_xen, - PROG_page, - PROG_mapping, -+ PROG_p2m, - PROG_done, - }; - -@@ -1056,6 +1057,11 @@ int domain_relinquish_resources(struct domain *d) - if ( ret ) - return ret; - -+ PROGRESS(p2m): -+ ret = p2m_teardown(d); -+ if ( ret ) -+ return ret; -+ - PROGRESS(done): - break; - -diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c -index cbeff90f4371..3bcd1e897e88 100644 ---- a/xen/arch/arm/p2m.c -+++ b/xen/arch/arm/p2m.c -@@ -1527,17 +1527,58 @@ static void p2m_free_vmid(struct domain *d) - spin_unlock(&vmid_alloc_lock); - } - --void p2m_teardown(struct domain *d) -+int p2m_teardown(struct domain *d) - { - struct p2m_domain *p2m = p2m_get_hostp2m(d); -+ unsigned long count = 0; - struct page_info *pg; -+ unsigned int i; -+ int rc = 0; -+ -+ p2m_write_lock(p2m); -+ -+ /* -+ * We are about to free the intermediate page-tables, so clear the -+ * root to prevent any walk to use them. -+ */ -+ for ( i = 0; i < P2M_ROOT_PAGES; i++ ) -+ clear_and_clean_page(p2m->root + i); -+ -+ /* -+ * The domain will not be scheduled anymore, so in theory we should -+ * not need to flush the TLBs. Do it for safety purpose. -+ * -+ * Note that all the devices have already been de-assigned. So we don't -+ * need to flush the IOMMU TLB here. -+ */ -+ p2m_force_tlb_flush_sync(p2m); -+ -+ while ( (pg = page_list_remove_head(&p2m->pages)) ) -+ { -+ free_domheap_page(pg); -+ count++; -+ /* Arbitrarily preempt every 512 iterations */ -+ if ( !(count % 512) && hypercall_preempt_check() ) -+ { -+ rc = -ERESTART; -+ break; -+ } -+ } -+ -+ p2m_write_unlock(p2m); -+ -+ return rc; -+} -+ -+void p2m_final_teardown(struct domain *d) -+{ -+ struct p2m_domain *p2m = p2m_get_hostp2m(d); - - /* p2m not actually initialized */ - if ( !p2m->domain ) - return; - -- while ( (pg = page_list_remove_head(&p2m->pages)) ) -- free_domheap_page(pg); -+ ASSERT(page_list_empty(&p2m->pages)); - - if ( p2m->root ) - free_domheap_pages(p2m->root, P2M_ROOT_ORDER); -diff --git a/xen/include/asm-arm/p2m.h b/xen/include/asm-arm/p2m.h -index 8f11d9c97b5d..b3ba83283e11 100644 ---- a/xen/include/asm-arm/p2m.h -+++ b/xen/include/asm-arm/p2m.h -@@ -192,8 +192,17 @@ void setup_virt_paging(void); - /* Init the datastructures for later use by the p2m code */ - int p2m_init(struct domain *d); - --/* Return all the p2m resources to Xen. */ --void p2m_teardown(struct domain *d); -+/* -+ * The P2M resources are freed in two parts: -+ * - p2m_teardown() will be called when relinquish the resources. It -+ * will free large resources (e.g. intermediate page-tables) that -+ * requires preemption. -+ * - p2m_final_teardown() will be called when domain struct is been -+ * freed. This *cannot* be preempted and therefore one small -+ * resources should be freed here. -+ */ -+int p2m_teardown(struct domain *d); -+void p2m_final_teardown(struct domain *d); - - /* - * Remove mapping refcount on each mapping page in the p2m --- -2.37.1 - diff --git a/xsa410-4.16-03.patch b/xsa410-4.16-03.patch deleted file mode 100644 index 7411d90..0000000 --- a/xsa410-4.16-03.patch +++ /dev/null @@ -1,113 +0,0 @@ -From: Roger Pau Monné -Subject: x86/p2m: add option to skip root pagetable removal in p2m_teardown() - -Add a new parameter to p2m_teardown() in order to select whether the -root page table should also be freed. Note that all users are -adjusted to pass the parameter to remove the root page tables, so -behavior is not modified. - -No functional change intended. - -This is part of CVE-2022-33746 / XSA-410. - -Suggested-by: Julien Grall -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich -Acked-by: Tim Deegan - ---- a/xen/include/asm-x86/p2m.h -+++ b/xen/include/asm-x86/p2m.h -@@ -574,7 +574,7 @@ int p2m_init(struct domain *d); - int p2m_alloc_table(struct p2m_domain *p2m); - - /* Return all the p2m resources to Xen. */ --void p2m_teardown(struct p2m_domain *p2m); -+void p2m_teardown(struct p2m_domain *p2m, bool remove_root); - void p2m_final_teardown(struct domain *d); - - /* Add a page to a domain's p2m table */ ---- a/xen/arch/x86/mm/hap/hap.c -+++ b/xen/arch/x86/mm/hap/hap.c -@@ -541,18 +541,18 @@ void hap_final_teardown(struct domain *d - } - - for ( i = 0; i < MAX_ALTP2M; i++ ) -- p2m_teardown(d->arch.altp2m_p2m[i]); -+ p2m_teardown(d->arch.altp2m_p2m[i], true); - } - - /* Destroy nestedp2m's first */ - for (i = 0; i < MAX_NESTEDP2M; i++) { -- p2m_teardown(d->arch.nested_p2m[i]); -+ p2m_teardown(d->arch.nested_p2m[i], true); - } - - if ( d->arch.paging.hap.total_pages != 0 ) - hap_teardown(d, NULL); - -- p2m_teardown(p2m_get_hostp2m(d)); -+ p2m_teardown(p2m_get_hostp2m(d), true); - /* Free any memory that the p2m teardown released */ - paging_lock(d); - hap_set_allocation(d, 0, NULL); ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -749,11 +749,11 @@ int p2m_alloc_table(struct p2m_domain *p - * hvm fixme: when adding support for pvh non-hardware domains, this path must - * cleanup any foreign p2m types (release refcnts on them). - */ --void p2m_teardown(struct p2m_domain *p2m) -+void p2m_teardown(struct p2m_domain *p2m, bool remove_root) - /* Return all the p2m pages to Xen. - * We know we don't have any extra mappings to these pages */ - { -- struct page_info *pg; -+ struct page_info *pg, *root_pg = NULL; - struct domain *d; - - if (p2m == NULL) -@@ -763,10 +763,22 @@ void p2m_teardown(struct p2m_domain *p2m - - p2m_lock(p2m); - ASSERT(atomic_read(&d->shr_pages) == 0); -- p2m->phys_table = pagetable_null(); -+ -+ if ( remove_root ) -+ p2m->phys_table = pagetable_null(); -+ else if ( !pagetable_is_null(p2m->phys_table) ) -+ { -+ root_pg = pagetable_get_page(p2m->phys_table); -+ clear_domain_page(pagetable_get_mfn(p2m->phys_table)); -+ } - - while ( (pg = page_list_remove_head(&p2m->pages)) ) -- d->arch.paging.free_page(d, pg); -+ if ( pg != root_pg ) -+ d->arch.paging.free_page(d, pg); -+ -+ if ( root_pg ) -+ page_list_add(root_pg, &p2m->pages); -+ - p2m_unlock(p2m); - } - ---- a/xen/arch/x86/mm/shadow/common.c -+++ b/xen/arch/x86/mm/shadow/common.c -@@ -2701,7 +2701,7 @@ int shadow_enable(struct domain *d, u32 - paging_unlock(d); - out_unlocked: - if ( rv != 0 && !pagetable_is_null(p2m_get_pagetable(p2m)) ) -- p2m_teardown(p2m); -+ p2m_teardown(p2m, true); - if ( rv != 0 && pg != NULL ) - { - pg->count_info &= ~PGC_count_mask; -@@ -2866,7 +2866,7 @@ void shadow_final_teardown(struct domain - shadow_teardown(d, NULL); - - /* It is now safe to pull down the p2m map. */ -- p2m_teardown(p2m_get_hostp2m(d)); -+ p2m_teardown(p2m_get_hostp2m(d), true); - /* Free any shadow memory that the p2m teardown released */ - paging_lock(d); - shadow_set_allocation(d, 0, NULL); diff --git a/xsa410-4.16-04.patch b/xsa410-4.16-04.patch deleted file mode 100644 index 2170b8e..0000000 --- a/xsa410-4.16-04.patch +++ /dev/null @@ -1,62 +0,0 @@ -From: Jan Beulich -Subject: x86/HAP: adjust monitor table related error handling - -hap_make_monitor_table() will return INVALID_MFN if it encounters an -error condition, but hap_update_paging_modes() wasn’t handling this -value, resulting in an inappropriate value being stored in -monitor_table. This would subsequently misguide at least -hap_vcpu_teardown(). Avoid this by bailing early. - -Further, when a domain has/was already crashed or (perhaps less -important as there's no such path known to lead here) is already dying, -avoid calling domain_crash() on it again - that's at best confusing. - -This is part of CVE-2022-33746 / XSA-410. - -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné - ---- a/xen/arch/x86/mm/hap/hap.c -+++ b/xen/arch/x86/mm/hap/hap.c -@@ -39,6 +39,7 @@ - #include - #include - #include -+#include - - #include "private.h" - -@@ -405,8 +406,13 @@ static mfn_t hap_make_monitor_table(stru - return m4mfn; - - oom: -- printk(XENLOG_G_ERR "out of memory building monitor pagetable\n"); -- domain_crash(d); -+ if ( !d->is_dying && -+ (!d->is_shutting_down || d->shutdown_code != SHUTDOWN_crash) ) -+ { -+ printk(XENLOG_G_ERR "%pd: out of memory building monitor pagetable\n", -+ d); -+ domain_crash(d); -+ } - return INVALID_MFN; - } - -@@ -766,6 +772,9 @@ static void hap_update_paging_modes(stru - if ( pagetable_is_null(v->arch.hvm.monitor_table) ) - { - mfn_t mmfn = hap_make_monitor_table(v); -+ -+ if ( mfn_eq(mmfn, INVALID_MFN) ) -+ goto unlock; - v->arch.hvm.monitor_table = pagetable_from_mfn(mmfn); - make_cr3(v, mmfn); - hvm_update_host_cr3(v); -@@ -774,6 +783,7 @@ static void hap_update_paging_modes(stru - /* CR3 is effectively updated by a mode change. Flush ASIDs, etc. */ - hap_update_cr3(v, 0, false); - -+ unlock: - paging_unlock(d); - put_gfn(d, cr3_gfn); - } diff --git a/xsa410-4.16-05.patch b/xsa410-4.16-05.patch deleted file mode 100644 index dc626c7..0000000 --- a/xsa410-4.16-05.patch +++ /dev/null @@ -1,60 +0,0 @@ -From: Jan Beulich -Subject: x86/shadow: tolerate failure of sh_set_toplevel_shadow() - -Subsequently sh_set_toplevel_shadow() will be adjusted to install a -blank entry in case prealloc fails. There are, in fact, pre-existing -error paths which would put in place a blank entry. The 4- and 2-level -code in sh_update_cr3(), however, assume the top level entry to be -valid. - -Hence bail from the function in the unlikely event that it's not. Note -that 3-level logic works differently: In particular a guest is free to -supply a PDPTR pointing at 4 non-present (or otherwise deemed invalid) -entries. The guest will crash, but we already cope with that. - -Really mfn_valid() is likely wrong to use in sh_set_toplevel_shadow(), -and it should instead be !mfn_eq(gmfn, INVALID_MFN). Avoid such a change -in security context, but add a respective assertion. - -This is part of CVE-2022-33746 / XSA-410. - -Signed-off-by: Jan Beulich -Acked-by: Tim Deegan -Reviewed-by: Andrew Cooper - ---- a/xen/arch/x86/mm/shadow/common.c -+++ b/xen/arch/x86/mm/shadow/common.c -@@ -2516,6 +2516,7 @@ void sh_set_toplevel_shadow(struct vcpu - /* Now figure out the new contents: is this a valid guest MFN? */ - if ( !mfn_valid(gmfn) ) - { -+ ASSERT(mfn_eq(gmfn, INVALID_MFN)); - new_entry = pagetable_null(); - goto install_new_entry; - } ---- a/xen/arch/x86/mm/shadow/multi.c -+++ b/xen/arch/x86/mm/shadow/multi.c -@@ -3312,6 +3312,11 @@ sh_update_cr3(struct vcpu *v, int do_loc - if ( sh_remove_write_access(d, gmfn, 4, 0) != 0 ) - guest_flush_tlb_mask(d, d->dirty_cpumask); - sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l4_shadow, sh_make_shadow); -+ if ( unlikely(pagetable_is_null(v->arch.paging.shadow.shadow_table[0])) ) -+ { -+ ASSERT(d->is_dying || d->is_shutting_down); -+ return; -+ } - if ( !shadow_mode_external(d) && !is_pv_32bit_domain(d) ) - { - mfn_t smfn = pagetable_get_mfn(v->arch.paging.shadow.shadow_table[0]); -@@ -3370,6 +3375,11 @@ sh_update_cr3(struct vcpu *v, int do_loc - if ( sh_remove_write_access(d, gmfn, 2, 0) != 0 ) - guest_flush_tlb_mask(d, d->dirty_cpumask); - sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l2_shadow, sh_make_shadow); -+ if ( unlikely(pagetable_is_null(v->arch.paging.shadow.shadow_table[0])) ) -+ { -+ ASSERT(d->is_dying || d->is_shutting_down); -+ return; -+ } - #else - #error This should never happen - #endif diff --git a/xsa410-4.16-06.patch b/xsa410-4.16-06.patch deleted file mode 100644 index 8c89eb3..0000000 --- a/xsa410-4.16-06.patch +++ /dev/null @@ -1,255 +0,0 @@ -From: Roger Pau Monné -Subject: x86/shadow: tolerate failure in shadow_prealloc() - -Prevent _shadow_prealloc() from calling BUG() when unable to fulfill -the pre-allocation and instead return true/false. Modify -shadow_prealloc() to crash the domain on allocation failure (if the -domain is not already dying), as shadow cannot operate normally after -that. Modify callers to also gracefully handle {_,}shadow_prealloc() -failing to fulfill the request. - -Note this in turn requires adjusting the callers of -sh_make_monitor_table() also to handle it returning INVALID_MFN. -sh_update_paging_modes() is also modified to add additional error -paths in case of allocation failure, some of those will return with -null monitor page tables (and the domain likely crashed). This is no -different that current error paths, but the newly introduced ones are -more likely to trigger. - -The now added failure points in sh_update_paging_modes() also require -that on some error return paths the previous structures are cleared, -and thus monitor table is null. - -While there adjust the 'type' parameter type of shadow_prealloc() to -unsigned int rather than u32. - -This is part of CVE-2022-33746 / XSA-410. - -Signed-off-by: Roger Pau Monné -Signed-off-by: Jan Beulich -Acked-by: Tim Deegan - ---- a/xen/arch/x86/mm/shadow/common.c -+++ b/xen/arch/x86/mm/shadow/common.c -@@ -36,6 +36,7 @@ - #include - #include - #include -+#include - #include "private.h" - - DEFINE_PER_CPU(uint32_t,trace_shadow_path_flags); -@@ -928,14 +929,15 @@ static inline void trace_shadow_prealloc - - /* Make sure there are at least count order-sized pages - * available in the shadow page pool. */ --static void _shadow_prealloc(struct domain *d, unsigned int pages) -+static bool __must_check _shadow_prealloc(struct domain *d, unsigned int pages) - { - struct vcpu *v; - struct page_info *sp, *t; - mfn_t smfn; - int i; - -- if ( d->arch.paging.shadow.free_pages >= pages ) return; -+ if ( d->arch.paging.shadow.free_pages >= pages ) -+ return true; - - /* Shouldn't have enabled shadows if we've no vcpus. */ - ASSERT(d->vcpu && d->vcpu[0]); -@@ -951,7 +953,8 @@ static void _shadow_prealloc(struct doma - sh_unpin(d, smfn); - - /* See if that freed up enough space */ -- if ( d->arch.paging.shadow.free_pages >= pages ) return; -+ if ( d->arch.paging.shadow.free_pages >= pages ) -+ return true; - } - - /* Stage two: all shadow pages are in use in hierarchies that are -@@ -974,7 +977,7 @@ static void _shadow_prealloc(struct doma - if ( d->arch.paging.shadow.free_pages >= pages ) - { - guest_flush_tlb_mask(d, d->dirty_cpumask); -- return; -+ return true; - } - } - } -@@ -987,7 +990,12 @@ static void _shadow_prealloc(struct doma - d->arch.paging.shadow.total_pages, - d->arch.paging.shadow.free_pages, - d->arch.paging.shadow.p2m_pages); -- BUG(); -+ -+ ASSERT(d->is_dying); -+ -+ guest_flush_tlb_mask(d, d->dirty_cpumask); -+ -+ return false; - } - - /* Make sure there are at least count pages of the order according to -@@ -995,9 +1003,19 @@ static void _shadow_prealloc(struct doma - * This must be called before any calls to shadow_alloc(). Since this - * will free existing shadows to make room, it must be called early enough - * to avoid freeing shadows that the caller is currently working on. */ --void shadow_prealloc(struct domain *d, u32 type, unsigned int count) -+bool shadow_prealloc(struct domain *d, unsigned int type, unsigned int count) - { -- return _shadow_prealloc(d, shadow_size(type) * count); -+ bool ret = _shadow_prealloc(d, shadow_size(type) * count); -+ -+ if ( !ret && !d->is_dying && -+ (!d->is_shutting_down || d->shutdown_code != SHUTDOWN_crash) ) -+ /* -+ * Failing to allocate memory required for shadow usage can only result in -+ * a domain crash, do it here rather that relying on every caller to do it. -+ */ -+ domain_crash(d); -+ -+ return ret; - } - - /* Deliberately free all the memory we can: this will tear down all of -@@ -1218,7 +1236,7 @@ void shadow_free(struct domain *d, mfn_t - static struct page_info * - shadow_alloc_p2m_page(struct domain *d) - { -- struct page_info *pg; -+ struct page_info *pg = NULL; - - /* This is called both from the p2m code (which never holds the - * paging lock) and the log-dirty code (which always does). */ -@@ -1236,16 +1254,18 @@ shadow_alloc_p2m_page(struct domain *d) - d->arch.paging.shadow.p2m_pages, - shadow_min_acceptable_pages(d)); - } -- paging_unlock(d); -- return NULL; -+ goto out; - } - -- shadow_prealloc(d, SH_type_p2m_table, 1); -+ if ( !shadow_prealloc(d, SH_type_p2m_table, 1) ) -+ goto out; -+ - pg = mfn_to_page(shadow_alloc(d, SH_type_p2m_table, 0)); - d->arch.paging.shadow.p2m_pages++; - d->arch.paging.shadow.total_pages--; - ASSERT(!page_get_owner(pg) && !(pg->count_info & PGC_count_mask)); - -+ out: - paging_unlock(d); - - return pg; -@@ -1336,7 +1356,9 @@ int shadow_set_allocation(struct domain - else if ( d->arch.paging.shadow.total_pages > pages ) - { - /* Need to return memory to domheap */ -- _shadow_prealloc(d, 1); -+ if ( !_shadow_prealloc(d, 1) ) -+ return -ENOMEM; -+ - sp = page_list_remove_head(&d->arch.paging.shadow.freelist); - ASSERT(sp); - /* -@@ -2334,12 +2356,13 @@ static void sh_update_paging_modes(struc - if ( mfn_eq(v->arch.paging.shadow.oos_snapshot[0], INVALID_MFN) ) - { - int i; -+ -+ if ( !shadow_prealloc(d, SH_type_oos_snapshot, SHADOW_OOS_PAGES) ) -+ return; -+ - for(i = 0; i < SHADOW_OOS_PAGES; i++) -- { -- shadow_prealloc(d, SH_type_oos_snapshot, 1); - v->arch.paging.shadow.oos_snapshot[i] = - shadow_alloc(d, SH_type_oos_snapshot, 0); -- } - } - #endif /* OOS */ - -@@ -2403,6 +2426,9 @@ static void sh_update_paging_modes(struc - mfn_t mmfn = sh_make_monitor_table( - v, v->arch.paging.mode->shadow.shadow_levels); - -+ if ( mfn_eq(mmfn, INVALID_MFN) ) -+ return; -+ - v->arch.hvm.monitor_table = pagetable_from_mfn(mmfn); - make_cr3(v, mmfn); - hvm_update_host_cr3(v); -@@ -2441,6 +2467,12 @@ static void sh_update_paging_modes(struc - v->arch.hvm.monitor_table = pagetable_null(); - new_mfn = sh_make_monitor_table( - v, v->arch.paging.mode->shadow.shadow_levels); -+ if ( mfn_eq(new_mfn, INVALID_MFN) ) -+ { -+ sh_destroy_monitor_table(v, old_mfn, -+ old_mode->shadow.shadow_levels); -+ return; -+ } - v->arch.hvm.monitor_table = pagetable_from_mfn(new_mfn); - SHADOW_PRINTK("new monitor table %"PRI_mfn "\n", - mfn_x(new_mfn)); -@@ -2526,7 +2558,12 @@ void sh_set_toplevel_shadow(struct vcpu - if ( !mfn_valid(smfn) ) - { - /* Make sure there's enough free shadow memory. */ -- shadow_prealloc(d, root_type, 1); -+ if ( !shadow_prealloc(d, root_type, 1) ) -+ { -+ new_entry = pagetable_null(); -+ goto install_new_entry; -+ } -+ - /* Shadow the page. */ - smfn = make_shadow(v, gmfn, root_type); - } ---- a/xen/arch/x86/mm/shadow/hvm.c -+++ b/xen/arch/x86/mm/shadow/hvm.c -@@ -700,7 +700,9 @@ mfn_t sh_make_monitor_table(const struct - ASSERT(!pagetable_get_pfn(v->arch.hvm.monitor_table)); - - /* Guarantee we can get the memory we need */ -- shadow_prealloc(d, SH_type_monitor_table, CONFIG_PAGING_LEVELS); -+ if ( !shadow_prealloc(d, SH_type_monitor_table, CONFIG_PAGING_LEVELS) ) -+ return INVALID_MFN; -+ - m4mfn = shadow_alloc(d, SH_type_monitor_table, 0); - mfn_to_page(m4mfn)->shadow_flags = 4; - ---- a/xen/arch/x86/mm/shadow/multi.c -+++ b/xen/arch/x86/mm/shadow/multi.c -@@ -2440,9 +2440,14 @@ static int sh_page_fault(struct vcpu *v, - * Preallocate shadow pages *before* removing writable accesses - * otherwhise an OOS L1 might be demoted and promoted again with - * writable mappings. */ -- shadow_prealloc(d, -- SH_type_l1_shadow, -- GUEST_PAGING_LEVELS < 4 ? 1 : GUEST_PAGING_LEVELS - 1); -+ if ( !shadow_prealloc(d, SH_type_l1_shadow, -+ GUEST_PAGING_LEVELS < 4 -+ ? 1 : GUEST_PAGING_LEVELS - 1) ) -+ { -+ paging_unlock(d); -+ put_gfn(d, gfn_x(gfn)); -+ return 0; -+ } - - rc = gw_remove_write_accesses(v, va, &gw); - ---- a/xen/arch/x86/mm/shadow/private.h -+++ b/xen/arch/x86/mm/shadow/private.h -@@ -383,7 +383,8 @@ void shadow_promote(struct domain *d, mf - void shadow_demote(struct domain *d, mfn_t gmfn, u32 type); - - /* Shadow page allocation functions */ --void shadow_prealloc(struct domain *d, u32 shadow_type, unsigned int count); -+bool __must_check shadow_prealloc(struct domain *d, unsigned int shadow_type, -+ unsigned int count); - mfn_t shadow_alloc(struct domain *d, - u32 shadow_type, - unsigned long backpointer); diff --git a/xsa410-4.16-07.patch b/xsa410-4.16-07.patch deleted file mode 100644 index 9eea91c..0000000 --- a/xsa410-4.16-07.patch +++ /dev/null @@ -1,82 +0,0 @@ -From: Roger Pau Monné -Subject: x86/p2m: refuse new allocations for dying domains - -This will in particular prevent any attempts to add entries to the p2m, -once - in a subsequent change - non-root entries have been removed. - -This is part of CVE-2022-33746 / XSA-410. - -Signed-off-by: Roger Pau Monné -Signed-off-by: Jan Beulich -Acked-by: Tim Deegan - ---- a/xen/arch/x86/mm/hap/hap.c -+++ b/xen/arch/x86/mm/hap/hap.c -@@ -245,6 +245,9 @@ static struct page_info *hap_alloc(struc - - ASSERT(paging_locked_by_me(d)); - -+ if ( unlikely(d->is_dying) ) -+ return NULL; -+ - pg = page_list_remove_head(&d->arch.paging.hap.freelist); - if ( unlikely(!pg) ) - return NULL; -@@ -281,7 +284,7 @@ static struct page_info *hap_alloc_p2m_p - d->arch.paging.hap.p2m_pages++; - ASSERT(!page_get_owner(pg) && !(pg->count_info & PGC_count_mask)); - } -- else if ( !d->arch.paging.p2m_alloc_failed ) -+ else if ( !d->arch.paging.p2m_alloc_failed && !d->is_dying ) - { - d->arch.paging.p2m_alloc_failed = 1; - dprintk(XENLOG_ERR, "d%i failed to allocate from HAP pool\n", ---- a/xen/arch/x86/mm/shadow/common.c -+++ b/xen/arch/x86/mm/shadow/common.c -@@ -939,6 +939,10 @@ static bool __must_check _shadow_preallo - if ( d->arch.paging.shadow.free_pages >= pages ) - return true; - -+ if ( unlikely(d->is_dying) ) -+ /* No reclaim when the domain is dying, teardown will take care of it. */ -+ return false; -+ - /* Shouldn't have enabled shadows if we've no vcpus. */ - ASSERT(d->vcpu && d->vcpu[0]); - -@@ -991,7 +995,7 @@ static bool __must_check _shadow_preallo - d->arch.paging.shadow.free_pages, - d->arch.paging.shadow.p2m_pages); - -- ASSERT(d->is_dying); -+ ASSERT_UNREACHABLE(); - - guest_flush_tlb_mask(d, d->dirty_cpumask); - -@@ -1005,10 +1009,13 @@ static bool __must_check _shadow_preallo - * to avoid freeing shadows that the caller is currently working on. */ - bool shadow_prealloc(struct domain *d, unsigned int type, unsigned int count) - { -- bool ret = _shadow_prealloc(d, shadow_size(type) * count); -+ bool ret; -+ -+ if ( unlikely(d->is_dying) ) -+ return false; - -- if ( !ret && !d->is_dying && -- (!d->is_shutting_down || d->shutdown_code != SHUTDOWN_crash) ) -+ ret = _shadow_prealloc(d, shadow_size(type) * count); -+ if ( !ret && (!d->is_shutting_down || d->shutdown_code != SHUTDOWN_crash) ) - /* - * Failing to allocate memory required for shadow usage can only result in - * a domain crash, do it here rather that relying on every caller to do it. -@@ -1238,6 +1245,9 @@ shadow_alloc_p2m_page(struct domain *d) - { - struct page_info *pg = NULL; - -+ if ( unlikely(d->is_dying) ) -+ return NULL; -+ - /* This is called both from the p2m code (which never holds the - * paging lock) and the log-dirty code (which always does). */ - paging_lock_recursive(d); diff --git a/xsa410-4.16-08.patch b/xsa410-4.16-08.patch deleted file mode 100644 index 92cdb49..0000000 --- a/xsa410-4.16-08.patch +++ /dev/null @@ -1,96 +0,0 @@ -From: Roger Pau Monné -Subject: x86/p2m: truly free paging pool memory for dying domains - -Modify {hap,shadow}_free to free the page immediately if the domain is -dying, so that pages don't accumulate in the pool when -{shadow,hap}_final_teardown() get called. This is to limit the amount of -work which needs to be done there (in a non-preemptable manner). - -Note the call to shadow_free() in shadow_free_p2m_page() is moved after -increasing total_pages, so that the decrease done in shadow_free() in -case the domain is dying doesn't underflow the counter, even if just for -a short interval. - -This is part of CVE-2022-33746 / XSA-410. - -Signed-off-by: Roger Pau Monné -Signed-off-by: Jan Beulich -Acked-by: Tim Deegan - ---- a/xen/arch/x86/mm/hap/hap.c -+++ b/xen/arch/x86/mm/hap/hap.c -@@ -265,6 +265,18 @@ static void hap_free(struct domain *d, m - - ASSERT(paging_locked_by_me(d)); - -+ /* -+ * For dying domains, actually free the memory here. This way less work is -+ * left to hap_final_teardown(), which cannot easily have preemption checks -+ * added. -+ */ -+ if ( unlikely(d->is_dying) ) -+ { -+ free_domheap_page(pg); -+ d->arch.paging.hap.total_pages--; -+ return; -+ } -+ - d->arch.paging.hap.free_pages++; - page_list_add_tail(pg, &d->arch.paging.hap.freelist); - } ---- a/xen/arch/x86/mm/shadow/common.c -+++ b/xen/arch/x86/mm/shadow/common.c -@@ -1187,6 +1187,7 @@ mfn_t shadow_alloc(struct domain *d, - void shadow_free(struct domain *d, mfn_t smfn) - { - struct page_info *next = NULL, *sp = mfn_to_page(smfn); -+ bool dying = ACCESS_ONCE(d->is_dying); - struct page_list_head *pin_list; - unsigned int pages; - u32 shadow_type; -@@ -1229,11 +1230,32 @@ void shadow_free(struct domain *d, mfn_t - * just before the allocator hands the page out again. */ - page_set_tlbflush_timestamp(sp); - perfc_decr(shadow_alloc_count); -- page_list_add_tail(sp, &d->arch.paging.shadow.freelist); -+ -+ /* -+ * For dying domains, actually free the memory here. This way less -+ * work is left to shadow_final_teardown(), which cannot easily have -+ * preemption checks added. -+ */ -+ if ( unlikely(dying) ) -+ { -+ /* -+ * The backpointer field (sh.back) used by shadow code aliases the -+ * domain owner field, unconditionally clear it here to avoid -+ * free_domheap_page() attempting to parse it. -+ */ -+ page_set_owner(sp, NULL); -+ free_domheap_page(sp); -+ } -+ else -+ page_list_add_tail(sp, &d->arch.paging.shadow.freelist); -+ - sp = next; - } - -- d->arch.paging.shadow.free_pages += pages; -+ if ( unlikely(dying) ) -+ d->arch.paging.shadow.total_pages -= pages; -+ else -+ d->arch.paging.shadow.free_pages += pages; - } - - /* Divert a page from the pool to be used by the p2m mapping. -@@ -1303,9 +1325,9 @@ shadow_free_p2m_page(struct domain *d, s - * paging lock) and the log-dirty code (which always does). */ - paging_lock_recursive(d); - -- shadow_free(d, page_to_mfn(pg)); - d->arch.paging.shadow.p2m_pages--; - d->arch.paging.shadow.total_pages++; -+ shadow_free(d, page_to_mfn(pg)); - - paging_unlock(d); - } diff --git a/xsa410-4.16-09.patch b/xsa410-4.16-09.patch deleted file mode 100644 index 60259e1..0000000 --- a/xsa410-4.16-09.patch +++ /dev/null @@ -1,159 +0,0 @@ -From: Roger Pau Monné -Subject: x86/p2m: free the paging memory pool preemptively - -The paging memory pool is currently freed in two different places: -from {shadow,hap}_teardown() via domain_relinquish_resources() and -from {shadow,hap}_final_teardown() via complete_domain_destroy(). -While the former does handle preemption, the later doesn't. - -Attempt to move as much p2m related freeing as possible to happen -before the call to {shadow,hap}_teardown(), so that most memory can be -freed in a preemptive way. In order to avoid causing issues to -existing callers leave the root p2m page tables set and free them in -{hap,shadow}_final_teardown(). Also modify {hap,shadow}_free to free -the page immediately if the domain is dying, so that pages don't -accumulate in the pool when {shadow,hap}_final_teardown() get called. - -Move altp2m_vcpu_disable_ve() to be done in hap_teardown(), as that's -the place where altp2m_active gets disabled now. - -This is part of CVE-2022-33746 / XSA-410. - -Signed-off-by: Roger Pau Monné -Signed-off-by: Jan Beulich -Acked-by: Tim Deegan - ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -38,7 +38,6 @@ - #include - #include - #include --#include - #include - #include - #include -@@ -2381,12 +2380,6 @@ int domain_relinquish_resources(struct d - vpmu_destroy(v); - } - -- if ( altp2m_active(d) ) -- { -- for_each_vcpu ( d, v ) -- altp2m_vcpu_disable_ve(v); -- } -- - if ( is_pv_domain(d) ) - { - for_each_vcpu ( d, v ) ---- a/xen/arch/x86/mm/hap/hap.c -+++ b/xen/arch/x86/mm/hap/hap.c -@@ -28,6 +28,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -546,24 +547,8 @@ void hap_final_teardown(struct domain *d - unsigned int i; - - if ( hvm_altp2m_supported() ) -- { -- d->arch.altp2m_active = 0; -- -- if ( d->arch.altp2m_eptp ) -- { -- free_xenheap_page(d->arch.altp2m_eptp); -- d->arch.altp2m_eptp = NULL; -- } -- -- if ( d->arch.altp2m_visible_eptp ) -- { -- free_xenheap_page(d->arch.altp2m_visible_eptp); -- d->arch.altp2m_visible_eptp = NULL; -- } -- - for ( i = 0; i < MAX_ALTP2M; i++ ) - p2m_teardown(d->arch.altp2m_p2m[i], true); -- } - - /* Destroy nestedp2m's first */ - for (i = 0; i < MAX_NESTEDP2M; i++) { -@@ -578,6 +563,8 @@ void hap_final_teardown(struct domain *d - paging_lock(d); - hap_set_allocation(d, 0, NULL); - ASSERT(d->arch.paging.hap.p2m_pages == 0); -+ ASSERT(d->arch.paging.hap.free_pages == 0); -+ ASSERT(d->arch.paging.hap.total_pages == 0); - paging_unlock(d); - } - -@@ -603,6 +590,7 @@ void hap_vcpu_teardown(struct vcpu *v) - void hap_teardown(struct domain *d, bool *preempted) - { - struct vcpu *v; -+ unsigned int i; - - ASSERT(d->is_dying); - ASSERT(d != current->domain); -@@ -611,6 +599,28 @@ void hap_teardown(struct domain *d, bool - for_each_vcpu ( d, v ) - hap_vcpu_teardown(v); - -+ /* Leave the root pt in case we get further attempts to modify the p2m. */ -+ if ( hvm_altp2m_supported() ) -+ { -+ if ( altp2m_active(d) ) -+ for_each_vcpu ( d, v ) -+ altp2m_vcpu_disable_ve(v); -+ -+ d->arch.altp2m_active = 0; -+ -+ FREE_XENHEAP_PAGE(d->arch.altp2m_eptp); -+ FREE_XENHEAP_PAGE(d->arch.altp2m_visible_eptp); -+ -+ for ( i = 0; i < MAX_ALTP2M; i++ ) -+ p2m_teardown(d->arch.altp2m_p2m[i], false); -+ } -+ -+ /* Destroy nestedp2m's after altp2m. */ -+ for ( i = 0; i < MAX_NESTEDP2M; i++ ) -+ p2m_teardown(d->arch.nested_p2m[i], false); -+ -+ p2m_teardown(p2m_get_hostp2m(d), false); -+ - paging_lock(d); /* Keep various asserts happy */ - - if ( d->arch.paging.hap.total_pages != 0 ) ---- a/xen/arch/x86/mm/shadow/common.c -+++ b/xen/arch/x86/mm/shadow/common.c -@@ -2824,8 +2824,17 @@ void shadow_teardown(struct domain *d, b - for_each_vcpu ( d, v ) - shadow_vcpu_teardown(v); - -+ p2m_teardown(p2m_get_hostp2m(d), false); -+ - paging_lock(d); - -+ /* -+ * Reclaim all shadow memory so that shadow_set_allocation() doesn't find -+ * in-use pages, as _shadow_prealloc() will no longer try to reclaim pages -+ * because the domain is dying. -+ */ -+ shadow_blow_tables(d); -+ - #if (SHADOW_OPTIMIZATIONS & (SHOPT_VIRTUAL_TLB|SHOPT_OUT_OF_SYNC)) - /* Free the virtual-TLB array attached to each vcpu */ - for_each_vcpu(d, v) -@@ -2946,6 +2955,9 @@ void shadow_final_teardown(struct domain - d->arch.paging.shadow.total_pages, - d->arch.paging.shadow.free_pages, - d->arch.paging.shadow.p2m_pages); -+ ASSERT(!d->arch.paging.shadow.total_pages); -+ ASSERT(!d->arch.paging.shadow.free_pages); -+ ASSERT(!d->arch.paging.shadow.p2m_pages); - paging_unlock(d); - } - diff --git a/xsa410-4.16-10.patch b/xsa410-4.16-10.patch deleted file mode 100644 index 258b7ff..0000000 --- a/xsa410-4.16-10.patch +++ /dev/null @@ -1,171 +0,0 @@ -From: Julien Grall -Subject: xen/x86: p2m: Add preemption in p2m_teardown() - -The list p2m->pages contain all the pages used by the P2M. On large -instance this can be quite large and the time spent to call -d->arch.paging.free_page() will take more than 1ms for a 80GB guest -on a Xen running in nested environment on a c5.metal. - -By extrapolation, it would take > 100ms for a 8TB guest (what we -current security support). So add some preemption in p2m_teardown() -and propagate to the callers. Note there are 3 places where -the preemption is not enabled: - - hap_final_teardown()/shadow_final_teardown(): We are - preventing update the P2M once the domain is dying (so - no more pages could be allocated) and most of the P2M pages - will be freed in preemptive manneer when relinquishing the - resources. So this is fine to disable preemption. - - shadow_enable(): This is fine because it will undo the allocation - that may have been made by p2m_alloc_table() (so only the root - page table). - -The preemption is arbitrarily checked every 1024 iterations. - -Note that with the current approach, Xen doesn't keep track on whether -the alt/nested P2Ms have been cleared. So there are some redundant work. -However, this is not expected to incurr too much overhead (the P2M lock -shouldn't be contended during teardown). So this is optimization is -left outside of the security event. - -This is part of CVE-2022-33746 / XSA-410. - -Signed-off-by: Julien Grall -Signed-off-by: Jan Beulich - ---- a/xen/include/asm-x86/p2m.h -+++ b/xen/include/asm-x86/p2m.h -@@ -574,7 +574,7 @@ int p2m_init(struct domain *d); - int p2m_alloc_table(struct p2m_domain *p2m); - - /* Return all the p2m resources to Xen. */ --void p2m_teardown(struct p2m_domain *p2m, bool remove_root); -+void p2m_teardown(struct p2m_domain *p2m, bool remove_root, bool *preempted); - void p2m_final_teardown(struct domain *d); - - /* Add a page to a domain's p2m table */ ---- a/xen/arch/x86/mm/hap/hap.c -+++ b/xen/arch/x86/mm/hap/hap.c -@@ -548,17 +548,17 @@ void hap_final_teardown(struct domain *d - - if ( hvm_altp2m_supported() ) - for ( i = 0; i < MAX_ALTP2M; i++ ) -- p2m_teardown(d->arch.altp2m_p2m[i], true); -+ p2m_teardown(d->arch.altp2m_p2m[i], true, NULL); - - /* Destroy nestedp2m's first */ - for (i = 0; i < MAX_NESTEDP2M; i++) { -- p2m_teardown(d->arch.nested_p2m[i], true); -+ p2m_teardown(d->arch.nested_p2m[i], true, NULL); - } - - if ( d->arch.paging.hap.total_pages != 0 ) - hap_teardown(d, NULL); - -- p2m_teardown(p2m_get_hostp2m(d), true); -+ p2m_teardown(p2m_get_hostp2m(d), true, NULL); - /* Free any memory that the p2m teardown released */ - paging_lock(d); - hap_set_allocation(d, 0, NULL); -@@ -612,14 +612,24 @@ void hap_teardown(struct domain *d, bool - FREE_XENHEAP_PAGE(d->arch.altp2m_visible_eptp); - - for ( i = 0; i < MAX_ALTP2M; i++ ) -- p2m_teardown(d->arch.altp2m_p2m[i], false); -+ { -+ p2m_teardown(d->arch.altp2m_p2m[i], false, preempted); -+ if ( preempted && *preempted ) -+ return; -+ } - } - - /* Destroy nestedp2m's after altp2m. */ - for ( i = 0; i < MAX_NESTEDP2M; i++ ) -- p2m_teardown(d->arch.nested_p2m[i], false); -+ { -+ p2m_teardown(d->arch.nested_p2m[i], false, preempted); -+ if ( preempted && *preempted ) -+ return; -+ } - -- p2m_teardown(p2m_get_hostp2m(d), false); -+ p2m_teardown(p2m_get_hostp2m(d), false, preempted); -+ if ( preempted && *preempted ) -+ return; - - paging_lock(d); /* Keep various asserts happy */ - ---- a/xen/arch/x86/mm/p2m.c -+++ b/xen/arch/x86/mm/p2m.c -@@ -749,12 +749,13 @@ int p2m_alloc_table(struct p2m_domain *p - * hvm fixme: when adding support for pvh non-hardware domains, this path must - * cleanup any foreign p2m types (release refcnts on them). - */ --void p2m_teardown(struct p2m_domain *p2m, bool remove_root) -+void p2m_teardown(struct p2m_domain *p2m, bool remove_root, bool *preempted) - /* Return all the p2m pages to Xen. - * We know we don't have any extra mappings to these pages */ - { - struct page_info *pg, *root_pg = NULL; - struct domain *d; -+ unsigned int i = 0; - - if (p2m == NULL) - return; -@@ -773,8 +774,19 @@ void p2m_teardown(struct p2m_domain *p2m - } - - while ( (pg = page_list_remove_head(&p2m->pages)) ) -- if ( pg != root_pg ) -- d->arch.paging.free_page(d, pg); -+ { -+ if ( pg == root_pg ) -+ continue; -+ -+ d->arch.paging.free_page(d, pg); -+ -+ /* Arbitrarily check preemption every 1024 iterations */ -+ if ( preempted && !(++i % 1024) && general_preempt_check() ) -+ { -+ *preempted = true; -+ break; -+ } -+ } - - if ( root_pg ) - page_list_add(root_pg, &p2m->pages); ---- a/xen/arch/x86/mm/shadow/common.c -+++ b/xen/arch/x86/mm/shadow/common.c -@@ -2770,8 +2770,12 @@ int shadow_enable(struct domain *d, u32 - out_locked: - paging_unlock(d); - out_unlocked: -+ /* -+ * This is fine to ignore the preemption here because only the root -+ * will be allocated by p2m_alloc_table(). -+ */ - if ( rv != 0 && !pagetable_is_null(p2m_get_pagetable(p2m)) ) -- p2m_teardown(p2m, true); -+ p2m_teardown(p2m, true, NULL); - if ( rv != 0 && pg != NULL ) - { - pg->count_info &= ~PGC_count_mask; -@@ -2824,7 +2828,9 @@ void shadow_teardown(struct domain *d, b - for_each_vcpu ( d, v ) - shadow_vcpu_teardown(v); - -- p2m_teardown(p2m_get_hostp2m(d), false); -+ p2m_teardown(p2m_get_hostp2m(d), false, preempted); -+ if ( preempted && *preempted ) -+ return; - - paging_lock(d); - -@@ -2945,7 +2951,7 @@ void shadow_final_teardown(struct domain - shadow_teardown(d, NULL); - - /* It is now safe to pull down the p2m map. */ -- p2m_teardown(p2m_get_hostp2m(d), true); -+ p2m_teardown(p2m_get_hostp2m(d), true, NULL); - /* Free any shadow memory that the p2m teardown released */ - paging_lock(d); - shadow_set_allocation(d, 0, NULL); diff --git a/xsa411.patch b/xsa411.patch deleted file mode 100644 index 50dcae4..0000000 --- a/xsa411.patch +++ /dev/null @@ -1,55 +0,0 @@ -From: Jan Beulich -Subject: gnttab: correct locking on transitive grant copy error path - -While the comment next to the lock dropping in preparation of -recursively calling acquire_grant_for_copy() mistakenly talks about the -rd == td case (excluded a few lines further up), the same concerns apply -to the calling of release_grant_for_copy() on a subsequent error path. - -This is CVE-2022-33748 / XSA-411. - -Fixes: ad48fb963dbf ("gnttab: fix transitive grant handling") -Signed-off-by: Jan Beulich ---- -v2: Extend code comment. - ---- a/xen/common/grant_table.c -+++ b/xen/common/grant_table.c -@@ -2622,9 +2622,8 @@ acquire_grant_for_copy( - trans_domid); - - /* -- * acquire_grant_for_copy() could take the lock on the -- * remote table (if rd == td), so we have to drop the lock -- * here and reacquire. -+ * acquire_grant_for_copy() will take the lock on the remote table, -+ * so we have to drop the lock here and reacquire. - */ - active_entry_release(act); - grant_read_unlock(rgt); -@@ -2661,11 +2660,25 @@ acquire_grant_for_copy( - act->trans_gref != trans_gref || - !act->is_sub_page)) ) - { -+ /* -+ * Like above for acquire_grant_for_copy() we need to drop and then -+ * re-acquire the locks here to prevent lock order inversion issues. -+ * Unlike for acquire_grant_for_copy() we don't need to re-check -+ * anything, as release_grant_for_copy() doesn't depend on the grant -+ * table entry: It only updates internal state and the status flags. -+ */ -+ active_entry_release(act); -+ grant_read_unlock(rgt); -+ - release_grant_for_copy(td, trans_gref, readonly); - rcu_unlock_domain(td); -+ -+ grant_read_lock(rgt); -+ act = active_entry_acquire(rgt, gref); - reduce_status_for_pin(rd, act, status, readonly); - active_entry_release(act); - grant_read_unlock(rgt); -+ - put_page(*page); - *page = NULL; - return ERESTART; diff --git a/xsa412-4.16.patch b/xsa412-4.16.patch deleted file mode 100644 index f37fc21..0000000 --- a/xsa412-4.16.patch +++ /dev/null @@ -1,245 +0,0 @@ -From: Andrew Cooper -Subject: x86/vmx: Revert "VMX: use a single, global APIC access page" - -The claim "No accesses would ever go to this page." is false. A consequence -of how Intel's APIC Acceleration works, and Xen's choice to have per-domain -P2Ms (rather than per-vCPU P2Ms) means that the APIC page is fully read-write -to any vCPU which is not in xAPIC mode. - -This reverts commit 58850b9074d3e7affdf3bc94c84e417ecfa4d165. - -This is XSA-412 / CVE-2022-42327. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c -index d429d76c18c9..3f4276531322 100644 ---- a/xen/arch/x86/hvm/vmx/vmx.c -+++ b/xen/arch/x86/hvm/vmx/vmx.c -@@ -66,7 +66,8 @@ boolean_param("force-ept", opt_force_ept); - static void vmx_ctxt_switch_from(struct vcpu *v); - static void vmx_ctxt_switch_to(struct vcpu *v); - --static int alloc_vlapic_mapping(void); -+static int vmx_alloc_vlapic_mapping(struct domain *d); -+static void vmx_free_vlapic_mapping(struct domain *d); - static void vmx_install_vlapic_mapping(struct vcpu *v); - static void vmx_update_guest_cr(struct vcpu *v, unsigned int cr, - unsigned int flags); -@@ -77,8 +78,6 @@ static int vmx_msr_read_intercept(unsigned int msr, uint64_t *msr_content); - static int vmx_msr_write_intercept(unsigned int msr, uint64_t msr_content); - static void vmx_invlpg(struct vcpu *v, unsigned long linear); - --static mfn_t __read_mostly apic_access_mfn = INVALID_MFN_INITIALIZER; -- - /* Values for domain's ->arch.hvm_domain.pi_ops.flags. */ - #define PI_CSW_FROM (1u << 0) - #define PI_CSW_TO (1u << 1) -@@ -402,6 +401,7 @@ static int vmx_domain_initialise(struct domain *d) - .to = vmx_ctxt_switch_to, - .tail = vmx_do_resume, - }; -+ int rc; - - d->arch.ctxt_switch = &csw; - -@@ -411,15 +411,24 @@ static int vmx_domain_initialise(struct domain *d) - */ - d->arch.hvm.vmx.exec_sp = is_hardware_domain(d) || opt_ept_exec_sp; - -+ if ( (rc = vmx_alloc_vlapic_mapping(d)) != 0 ) -+ return rc; -+ - return 0; - } - -+static void vmx_domain_relinquish_resources(struct domain *d) -+{ -+ vmx_free_vlapic_mapping(d); -+} -+ - static void domain_creation_finished(struct domain *d) - { - gfn_t gfn = gaddr_to_gfn(APIC_DEFAULT_PHYS_BASE); -+ mfn_t apic_access_mfn = d->arch.hvm.vmx.apic_access_mfn; - bool ipat; - -- if ( !has_vlapic(d) || mfn_eq(apic_access_mfn, INVALID_MFN) ) -+ if ( mfn_eq(apic_access_mfn, _mfn(0)) ) - return; - - ASSERT(epte_get_entry_emt(d, gfn, apic_access_mfn, 0, &ipat, -@@ -2481,6 +2490,7 @@ static struct hvm_function_table __initdata vmx_function_table = { - .cpu_up_prepare = vmx_cpu_up_prepare, - .cpu_dead = vmx_cpu_dead, - .domain_initialise = vmx_domain_initialise, -+ .domain_relinquish_resources = vmx_domain_relinquish_resources, - .domain_creation_finished = domain_creation_finished, - .vcpu_initialise = vmx_vcpu_initialise, - .vcpu_destroy = vmx_vcpu_destroy, -@@ -2731,7 +2741,7 @@ const struct hvm_function_table * __init start_vmx(void) - { - set_in_cr4(X86_CR4_VMXE); - -- if ( vmx_vmcs_init() || alloc_vlapic_mapping() ) -+ if ( vmx_vmcs_init() ) - { - printk("VMX: failed to initialise.\n"); - return NULL; -@@ -3305,36 +3315,55 @@ static int vmx_msr_read_intercept(unsigned int msr, uint64_t *msr_content) - return X86EMUL_EXCEPTION; - } - --static int __init alloc_vlapic_mapping(void) -+static int vmx_alloc_vlapic_mapping(struct domain *d) - { - struct page_info *pg; - mfn_t mfn; - -- if ( !cpu_has_vmx_virtualize_apic_accesses ) -+ if ( !has_vlapic(d) || !cpu_has_vmx_virtualize_apic_accesses ) - return 0; - -- pg = alloc_domheap_page(NULL, 0); -+ pg = alloc_domheap_page(d, MEMF_no_refcount); - if ( !pg ) - return -ENOMEM; - -- /* -- * Signal to shadow code that this page cannot be refcounted. This also -- * makes epte_get_entry_emt() recognize this page as "special". -- */ -- page_suppress_refcounting(pg); -+ if ( !get_page_and_type(pg, d, PGT_writable_page) ) -+ { -+ /* -+ * The domain can't possibly know about this page yet, so failure -+ * here is a clear indication of something fishy going on. -+ */ -+ domain_crash(d); -+ return -ENODATA; -+ } - - mfn = page_to_mfn(pg); - clear_domain_page(mfn); -- apic_access_mfn = mfn; -+ d->arch.hvm.vmx.apic_access_mfn = mfn; - - return 0; - } - -+static void vmx_free_vlapic_mapping(struct domain *d) -+{ -+ mfn_t mfn = d->arch.hvm.vmx.apic_access_mfn; -+ -+ d->arch.hvm.vmx.apic_access_mfn = _mfn(0); -+ if ( !mfn_eq(mfn, _mfn(0)) ) -+ { -+ struct page_info *pg = mfn_to_page(mfn); -+ -+ put_page_alloc_ref(pg); -+ put_page_and_type(pg); -+ } -+} -+ - static void vmx_install_vlapic_mapping(struct vcpu *v) - { -+ mfn_t apic_access_mfn = v->domain->arch.hvm.vmx.apic_access_mfn; - paddr_t virt_page_ma, apic_page_ma; - -- if ( !has_vlapic(v->domain) || mfn_eq(apic_access_mfn, INVALID_MFN) ) -+ if ( mfn_eq(apic_access_mfn, _mfn(0)) ) - return; - - ASSERT(cpu_has_vmx_virtualize_apic_accesses); -diff --git a/xen/arch/x86/mm/shadow/set.c b/xen/arch/x86/mm/shadow/set.c -index 87e9c6eeb219..bd6c68b547c9 100644 ---- a/xen/arch/x86/mm/shadow/set.c -+++ b/xen/arch/x86/mm/shadow/set.c -@@ -101,14 +101,6 @@ shadow_get_page_from_l1e(shadow_l1e_t sl1e, struct domain *d, p2m_type_t type) - owner = page_get_owner(pg); - } - -- /* -- * Check whether refcounting is suppressed on this page. For example, -- * VMX'es APIC access MFN is just a surrogate page. It doesn't actually -- * get accessed, and hence there's no need to refcount it. -- */ -- if ( pg && page_refcounting_suppressed(pg) ) -- return 0; -- - if ( owner == dom_io ) - owner = NULL; - -diff --git a/xen/arch/x86/mm/shadow/types.h b/xen/arch/x86/mm/shadow/types.h -index 6970e7d6ea4a..814a4018535a 100644 ---- a/xen/arch/x86/mm/shadow/types.h -+++ b/xen/arch/x86/mm/shadow/types.h -@@ -276,16 +276,9 @@ int shadow_set_l4e(struct domain *d, shadow_l4e_t *sl4e, - static void inline - shadow_put_page_from_l1e(shadow_l1e_t sl1e, struct domain *d) - { -- mfn_t mfn = shadow_l1e_get_mfn(sl1e); -- - if ( !shadow_mode_refcounts(d) ) - return; - -- if ( mfn_valid(mfn) && -- /* See the respective comment in shadow_get_page_from_l1e(). */ -- page_refcounting_suppressed(mfn_to_page(mfn)) ) -- return; -- - put_page_from_l1e(sl1e, d); - } - -diff --git a/xen/include/asm-x86/hvm/vmx/vmcs.h b/xen/include/asm-x86/hvm/vmx/vmcs.h -index 03c9ccf627ab..8073af323b96 100644 ---- a/xen/include/asm-x86/hvm/vmx/vmcs.h -+++ b/xen/include/asm-x86/hvm/vmx/vmcs.h -@@ -58,6 +58,7 @@ struct ept_data { - #define _VMX_DOMAIN_PML_ENABLED 0 - #define VMX_DOMAIN_PML_ENABLED (1ul << _VMX_DOMAIN_PML_ENABLED) - struct vmx_domain { -+ mfn_t apic_access_mfn; - /* VMX_DOMAIN_* */ - unsigned int status; - -diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h -index 7bdf9c2290d8..e1bcea57a8f5 100644 ---- a/xen/include/asm-x86/mm.h -+++ b/xen/include/asm-x86/mm.h -@@ -83,7 +83,7 @@ - #define PGC_state_offlined PG_mask(2, 6) - #define PGC_state_free PG_mask(3, 6) - #define page_state_is(pg, st) (((pg)->count_info&PGC_state) == PGC_state_##st) --/* Page is not reference counted (see below for caveats) */ -+/* Page is not reference counted */ - #define _PGC_extra PG_shift(7) - #define PGC_extra PG_mask(1, 7) - -@@ -375,24 +375,6 @@ void zap_ro_mpt(mfn_t mfn); - - bool is_iomem_page(mfn_t mfn); - --/* -- * Pages with no owner which may get passed to functions wanting to -- * refcount them can be marked PGC_extra to bypass this refcounting (which -- * would fail due to the lack of an owner). -- * -- * (For pages with owner PGC_extra has different meaning.) -- */ --static inline void page_suppress_refcounting(struct page_info *pg) --{ -- ASSERT(!page_get_owner(pg)); -- pg->count_info |= PGC_extra; --} -- --static inline bool page_refcounting_suppressed(const struct page_info *pg) --{ -- return !page_get_owner(pg) && (pg->count_info & PGC_extra); --} -- - struct platform_bad_page { - unsigned long mfn; - unsigned int order; diff --git a/xsa414.patch b/xsa414.patch deleted file mode 100644 index 27ab0c2..0000000 --- a/xsa414.patch +++ /dev/null @@ -1,112 +0,0 @@ -From: Julien Grall -Subject: tools/xenstore: create_node: Don't defer work to undo any changes on - failure - -XSA-115 extended destroy_node() to update the node accounting for the -connection. The implementation is assuming the connection is the parent -of the node, however all the nodes are allocated using a separate context -(see process_message()). This will result to crash (or corrupt) xenstored -as the pointer is wrongly used. - -In case of an error, any changes to the database or update to the -accounting will now be reverted in create_node() by calling directly -destroy_node(). This has the nice advantage to remove the loop to unset -the destructors in case of success. - -Take the opportunity to free the nodes right now as they are not -going to be reachable (the function returns NULL) and are just wasting -resources. - -This is XSA-414 / CVE-2022-42309. - -Reported-by: Julien Grall -Fixes: 0bfb2101f243 ("tools/xenstore: fix node accounting after failed node creation") -Signed-off-by: Julien Grall -Reviewed-by: Juergen Gross - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 8867f93431d4..c30d14cbf2ab 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1084,9 +1084,8 @@ static struct node *construct_node(struct connection *conn, const void *ctx, - return NULL; - } - --static int destroy_node(void *_node) -+static int destroy_node(struct connection *conn, struct node *node) - { -- struct node *node = _node; - TDB_DATA key; - - if (streq(node->name, "/")) -@@ -1095,7 +1094,7 @@ static int destroy_node(void *_node) - set_tdb_key(node->name, &key); - tdb_delete(tdb_ctx, key); - -- domain_entry_dec(talloc_parent(node), node); -+ domain_entry_dec(conn, node); - - return 0; - } -@@ -1104,7 +1103,8 @@ static struct node *create_node(struct connection *conn, const void *ctx, - const char *name, - void *data, unsigned int datalen) - { -- struct node *node, *i; -+ struct node *node, *i, *j; -+ int ret; - - node = construct_node(conn, ctx, name); - if (!node) -@@ -1126,23 +1126,40 @@ static struct node *create_node(struct connection *conn, const void *ctx, - /* i->parent is set for each new node, so check quota. */ - if (i->parent && - domain_entry(conn) >= quota_nb_entry_per_domain) { -- errno = ENOSPC; -- return NULL; -+ ret = ENOSPC; -+ goto err; - } -- if (write_node(conn, i, false)) -- return NULL; - -- /* Account for new node, set destructor for error case. */ -- if (i->parent) { -+ ret = write_node(conn, i, false); -+ if (ret) -+ goto err; -+ -+ /* Account for new node */ -+ if (i->parent) - domain_entry_inc(conn, i); -- talloc_set_destructor(i, destroy_node); -- } - } - -- /* OK, now remove destructors so they stay around */ -- for (i = node; i->parent; i = i->parent) -- talloc_set_destructor(i, NULL); - return node; -+ -+err: -+ /* -+ * We failed to update TDB for some of the nodes. Undo any work that -+ * have already been done. -+ */ -+ for (j = node; j != i; j = j->parent) -+ destroy_node(conn, j); -+ -+ /* We don't need to keep the nodes around, so free them. */ -+ i = node; -+ while (i) { -+ j = i; -+ i = i->parent; -+ talloc_free(j); -+ } -+ -+ errno = ret; -+ -+ return NULL; - } - - /* path, data... */ diff --git a/xsa415.patch b/xsa415.patch deleted file mode 100644 index b6f6971..0000000 --- a/xsa415.patch +++ /dev/null @@ -1,134 +0,0 @@ -From: Julien Grall -Subject: tools/xenstore: Fail a transaction if it is not possible to create a - node - -Commit f2bebf72c4d5 "xenstore: rework of transaction handling" moved -out from copying the entire database everytime a new transaction is -opened to track the list of nodes changed. - -The content of all the nodes accessed during a transaction will be -temporarily stored in TDB using a different key. - -The function create_node() may write/update multiple nodes if the child -doesn't exist. In case of a failure, the function will revert any -changes (this include any update to TDB). Unfortunately, the function -which reverts the changes (i.e. destroy_node()) will not use the correct -key to delete any update or even request the transaction to fail. - -This means that if a client decide to go ahead with committing the -transaction, orphan nodes will be created because they were not linked -to an existing node (create_node() will write the nodes backwards). - -Once some nodes have been partially updated in a transaction, it is not -easily possible to undo any changes. So rather than continuing and hit -weird issue while committing, it is much saner to fail the transaction. - -This will have an impact on any client that decides to commit even if it -can't write a node. Although, it is not clear why a normal client would -want to do that... - -Lastly, update destroy_node() to use the correct key for deleting the -node. Rather than recreating it (this will allocate memory and -therefore fail), stash the key in the structure node. - -This is XSA-415 / CVE-2022-42310. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Juergen Gross - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index c30d14cbf2ab..55b79e4c032e 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -562,15 +562,17 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, - return 0; - } - -+/* -+ * Write the node. If the node is written, caller can find the key used in -+ * node->key. This can later be used if the change needs to be reverted. -+ */ - static int write_node(struct connection *conn, struct node *node, - bool no_quota_check) - { -- TDB_DATA key; -- -- if (access_node(conn, node, NODE_ACCESS_WRITE, &key)) -+ if (access_node(conn, node, NODE_ACCESS_WRITE, &node->key)) - return errno; - -- return write_node_raw(conn, &key, node, no_quota_check); -+ return write_node_raw(conn, &node->key, node, no_quota_check); - } - - unsigned int perm_for_conn(struct connection *conn, -@@ -1086,16 +1088,21 @@ static struct node *construct_node(struct connection *conn, const void *ctx, - - static int destroy_node(struct connection *conn, struct node *node) - { -- TDB_DATA key; -- - if (streq(node->name, "/")) - corrupt(NULL, "Destroying root node!"); - -- set_tdb_key(node->name, &key); -- tdb_delete(tdb_ctx, key); -+ tdb_delete(tdb_ctx, node->key); - - domain_entry_dec(conn, node); - -+ /* -+ * It is not possible to easily revert the changes in a transaction. -+ * So if the failure happens in a transaction, mark it as fail to -+ * prevent any commit. -+ */ -+ if ( conn->transaction ) -+ fail_transaction(conn->transaction); -+ - return 0; - } - -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index 742812a97469..7d0fe77e7989 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -155,6 +155,8 @@ struct node_perms { - - struct node { - const char *name; -+ /* Key used to update TDB */ -+ TDB_DATA key; - - /* Parent (optional) */ - struct node *parent; -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index cd07fb0f218b..faf6c930e42a 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -580,6 +580,11 @@ void transaction_entry_dec(struct transaction *trans, unsigned int domid) - list_add_tail(&d->list, &trans->changed_domains); - } - -+void fail_transaction(struct transaction *trans) -+{ -+ trans->fail = true; -+} -+ - void conn_delete_all_transactions(struct connection *conn) - { - struct transaction *trans; -diff --git a/tools/xenstore/xenstored_transaction.h b/tools/xenstore/xenstored_transaction.h -index 43a162bea3f3..14062730e3c9 100644 ---- a/tools/xenstore/xenstored_transaction.h -+++ b/tools/xenstore/xenstored_transaction.h -@@ -46,6 +46,9 @@ int access_node(struct connection *conn, struct node *node, - int transaction_prepend(struct connection *conn, const char *name, - TDB_DATA *key); - -+/* Mark the transaction as failed. This will prevent it to be committed. */ -+void fail_transaction(struct transaction *trans); -+ - void conn_delete_all_transactions(struct connection *conn); - int check_transactions(struct hashtable *hash); - diff --git a/xsa416-4.16.patch b/xsa416-4.16.patch deleted file mode 100644 index 0df409a..0000000 --- a/xsa416-4.16.patch +++ /dev/null @@ -1,704 +0,0 @@ -From 80d128b14482d2e9342184d2d9949367851c4d14 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:10 +0200 -Subject: tools/xenstore: don't use conn->in as context for temporary - allocations - -Using the struct buffered data pointer of the current processed request -for temporary data allocations has a major drawback: the used area (and -with that the temporary data) is freed only after the response of the -request has been written to the ring page or has been read via the -socket. This can happen much later in case a guest isn't reading its -responses fast enough. - -As the temporary data can be safely freed after creating the response, -add a temporary context for that purpose and use that for allocating -the temporary memory, as it was already the case before commit -cc0612464896 ("xenstore: add small default data buffer to internal -struct"). - -Some sub-functions need to gain the "const" attribute for the talloc -context. - -This is XSA-416 / CVE-2022-42319. - -Reported-by: Julien Grall -Fixes: cc0612464896 ("xenstore: add small default data buffer to internal struct") -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_control.c b/tools/xenstore/xenstored_control.c -index 1031a81c3874..d0350c6ad861 100644 ---- a/tools/xenstore/xenstored_control.c -+++ b/tools/xenstore/xenstored_control.c -@@ -155,7 +155,7 @@ bool lu_is_pending(void) - - struct cmd_s { - char *cmd; -- int (*func)(void *, struct connection *, char **, int); -+ int (*func)(const void *, struct connection *, char **, int); - char *pars; - /* - * max_pars can be used to limit the size of the parameter vector, -@@ -167,7 +167,7 @@ struct cmd_s { - unsigned int max_pars; - }; - --static int do_control_check(void *ctx, struct connection *conn, -+static int do_control_check(const void *ctx, struct connection *conn, - char **vec, int num) - { - if (num) -@@ -179,7 +179,7 @@ static int do_control_check(void *ctx, struct connection *conn, - return 0; - } - --static int do_control_log(void *ctx, struct connection *conn, -+static int do_control_log(const void *ctx, struct connection *conn, - char **vec, int num) - { - if (num != 1) -@@ -281,7 +281,7 @@ static int quota_get(const void *ctx, struct connection *conn, - return domain_get_quota(ctx, conn, atoi(vec[0])); - } - --static int do_control_quota(void *ctx, struct connection *conn, -+static int do_control_quota(const void *ctx, struct connection *conn, - char **vec, int num) - { - if (num == 0) -@@ -293,7 +293,7 @@ static int do_control_quota(void *ctx, struct connection *conn, - return quota_get(ctx, conn, vec, num); - } - --static int do_control_quota_s(void *ctx, struct connection *conn, -+static int do_control_quota_s(const void *ctx, struct connection *conn, - char **vec, int num) - { - if (num == 0) -@@ -306,7 +306,7 @@ static int do_control_quota_s(void *ctx, struct connection *conn, - } - - #ifdef __MINIOS__ --static int do_control_memreport(void *ctx, struct connection *conn, -+static int do_control_memreport(const void *ctx, struct connection *conn, - char **vec, int num) - { - if (num) -@@ -318,7 +318,7 @@ static int do_control_memreport(void *ctx, struct connection *conn, - return 0; - } - #else --static int do_control_logfile(void *ctx, struct connection *conn, -+static int do_control_logfile(const void *ctx, struct connection *conn, - char **vec, int num) - { - if (num != 1) -@@ -333,7 +333,7 @@ static int do_control_logfile(void *ctx, struct connection *conn, - return 0; - } - --static int do_control_memreport(void *ctx, struct connection *conn, -+static int do_control_memreport(const void *ctx, struct connection *conn, - char **vec, int num) - { - FILE *fp; -@@ -373,7 +373,7 @@ static int do_control_memreport(void *ctx, struct connection *conn, - } - #endif - --static int do_control_print(void *ctx, struct connection *conn, -+static int do_control_print(const void *ctx, struct connection *conn, - char **vec, int num) - { - if (num != 1) -@@ -875,7 +875,7 @@ static const char *lu_start(const void *ctx, struct connection *conn, - return NULL; - } - --static int do_control_lu(void *ctx, struct connection *conn, -+static int do_control_lu(const void *ctx, struct connection *conn, - char **vec, int num) - { - const char *ret = NULL; -@@ -922,7 +922,7 @@ static int do_control_lu(void *ctx, struct connection *conn, - } - #endif - --static int do_control_help(void *, struct connection *, char **, int); -+static int do_control_help(const void *, struct connection *, char **, int); - - static struct cmd_s cmds[] = { - { "check", do_control_check, "" }, -@@ -961,7 +961,7 @@ static struct cmd_s cmds[] = { - { "help", do_control_help, "" }, - }; - --static int do_control_help(void *ctx, struct connection *conn, -+static int do_control_help(const void *ctx, struct connection *conn, - char **vec, int num) - { - int cmd, len = 0; -@@ -997,7 +997,8 @@ static int do_control_help(void *ctx, struct connection *conn, - return 0; - } - --int do_control(struct connection *conn, struct buffered_data *in) -+int do_control(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - unsigned int cmd, num, off; - char **vec = NULL; -@@ -1017,11 +1018,11 @@ int do_control(struct connection *conn, struct buffered_data *in) - num = xs_count_strings(in->buffer, in->used); - if (cmds[cmd].max_pars) - num = min(num, cmds[cmd].max_pars); -- vec = talloc_array(in, char *, num); -+ vec = talloc_array(ctx, char *, num); - if (!vec) - return ENOMEM; - if (get_strings(in, vec, num) < num) - return EIO; - -- return cmds[cmd].func(in, conn, vec + 1, num - 1); -+ return cmds[cmd].func(ctx, conn, vec + 1, num - 1); - } -diff --git a/tools/xenstore/xenstored_control.h b/tools/xenstore/xenstored_control.h -index 98b6fbcea2b1..a8cb76559ba1 100644 ---- a/tools/xenstore/xenstored_control.h -+++ b/tools/xenstore/xenstored_control.h -@@ -16,7 +16,8 @@ - along with this program; If not, see . - */ - --int do_control(struct connection *conn, struct buffered_data *in); -+int do_control(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - void lu_read_state(void); - - struct connection *lu_get_connection(void); -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 16504de42017..411cc0e44714 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1248,11 +1248,13 @@ static struct node *get_node_canonicalized(struct connection *conn, - return get_node(conn, ctx, *canonical_name, perm); - } - --static int send_directory(struct connection *conn, struct buffered_data *in) -+static int send_directory(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct node *node; - -- node = get_node_canonicalized(conn, in, onearg(in), NULL, XS_PERM_READ); -+ node = get_node_canonicalized(conn, ctx, onearg(in), NULL, -+ XS_PERM_READ); - if (!node) - return errno; - -@@ -1261,7 +1263,7 @@ static int send_directory(struct connection *conn, struct buffered_data *in) - return 0; - } - --static int send_directory_part(struct connection *conn, -+static int send_directory_part(const void *ctx, struct connection *conn, - struct buffered_data *in) - { - unsigned int off, len, maxlen, genlen; -@@ -1273,7 +1275,8 @@ static int send_directory_part(struct connection *conn, - return EINVAL; - - /* First arg is node name. */ -- node = get_node_canonicalized(conn, in, in->buffer, NULL, XS_PERM_READ); -+ node = get_node_canonicalized(conn, ctx, in->buffer, NULL, -+ XS_PERM_READ); - if (!node) - return errno; - -@@ -1300,7 +1303,7 @@ static int send_directory_part(struct connection *conn, - break; - } - -- data = talloc_array(in, char, genlen + len + 1); -+ data = talloc_array(ctx, char, genlen + len + 1); - if (!data) - return ENOMEM; - -@@ -1316,11 +1319,13 @@ static int send_directory_part(struct connection *conn, - return 0; - } - --static int do_read(struct connection *conn, struct buffered_data *in) -+static int do_read(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct node *node; - -- node = get_node_canonicalized(conn, in, onearg(in), NULL, XS_PERM_READ); -+ node = get_node_canonicalized(conn, ctx, onearg(in), NULL, -+ XS_PERM_READ); - if (!node) - return errno; - -@@ -1510,7 +1515,8 @@ static struct node *create_node(struct connection *conn, const void *ctx, - } - - /* path, data... */ --static int do_write(struct connection *conn, struct buffered_data *in) -+static int do_write(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - unsigned int offset, datalen; - struct node *node; -@@ -1524,12 +1530,12 @@ static int do_write(struct connection *conn, struct buffered_data *in) - offset = strlen(vec[0]) + 1; - datalen = in->used - offset; - -- node = get_node_canonicalized(conn, in, vec[0], &name, XS_PERM_WRITE); -+ node = get_node_canonicalized(conn, ctx, vec[0], &name, XS_PERM_WRITE); - if (!node) { - /* No permissions, invalid input? */ - if (errno != ENOENT) - return errno; -- node = create_node(conn, in, name, in->buffer + offset, -+ node = create_node(conn, ctx, name, in->buffer + offset, - datalen); - if (!node) - return errno; -@@ -1540,18 +1546,19 @@ static int do_write(struct connection *conn, struct buffered_data *in) - return errno; - } - -- fire_watches(conn, in, name, node, false, NULL); -+ fire_watches(conn, ctx, name, node, false, NULL); - send_ack(conn, XS_WRITE); - - return 0; - } - --static int do_mkdir(struct connection *conn, struct buffered_data *in) -+static int do_mkdir(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct node *node; - char *name; - -- node = get_node_canonicalized(conn, in, onearg(in), &name, -+ node = get_node_canonicalized(conn, ctx, onearg(in), &name, - XS_PERM_WRITE); - - /* If it already exists, fine. */ -@@ -1561,10 +1568,10 @@ static int do_mkdir(struct connection *conn, struct buffered_data *in) - return errno; - if (!name) - return ENOMEM; -- node = create_node(conn, in, name, NULL, 0); -+ node = create_node(conn, ctx, name, NULL, 0); - if (!node) - return errno; -- fire_watches(conn, in, name, node, false, NULL); -+ fire_watches(conn, ctx, name, node, false, NULL); - } - send_ack(conn, XS_MKDIR); - -@@ -1662,24 +1669,25 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, - } - - --static int do_rm(struct connection *conn, struct buffered_data *in) -+static int do_rm(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct node *node; - int ret; - char *name; - char *parentname; - -- node = get_node_canonicalized(conn, in, onearg(in), &name, -+ node = get_node_canonicalized(conn, ctx, onearg(in), &name, - XS_PERM_WRITE); - if (!node) { - /* Didn't exist already? Fine, if parent exists. */ - if (errno == ENOENT) { - if (!name) - return ENOMEM; -- parentname = get_parent(in, name); -+ parentname = get_parent(ctx, name); - if (!parentname) - return errno; -- node = read_node(conn, in, parentname); -+ node = read_node(conn, ctx, parentname); - if (node) { - send_ack(conn, XS_RM); - return 0; -@@ -1694,7 +1702,7 @@ static int do_rm(struct connection *conn, struct buffered_data *in) - if (streq(name, "/")) - return EINVAL; - -- ret = _rm(conn, in, node, name); -+ ret = _rm(conn, ctx, node, name); - if (ret) - return ret; - -@@ -1704,13 +1712,15 @@ static int do_rm(struct connection *conn, struct buffered_data *in) - } - - --static int do_get_perms(struct connection *conn, struct buffered_data *in) -+static int do_get_perms(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct node *node; - char *strings; - unsigned int len; - -- node = get_node_canonicalized(conn, in, onearg(in), NULL, XS_PERM_READ); -+ node = get_node_canonicalized(conn, ctx, onearg(in), NULL, -+ XS_PERM_READ); - if (!node) - return errno; - -@@ -1723,7 +1733,8 @@ static int do_get_perms(struct connection *conn, struct buffered_data *in) - return 0; - } - --static int do_set_perms(struct connection *conn, struct buffered_data *in) -+static int do_set_perms(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct node_perms perms, old_perms; - char *name, *permstr; -@@ -1740,7 +1751,7 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - - permstr = in->buffer + strlen(in->buffer) + 1; - -- perms.p = talloc_array(in, struct xs_permissions, perms.num); -+ perms.p = talloc_array(ctx, struct xs_permissions, perms.num); - if (!perms.p) - return ENOMEM; - if (!xs_strings_to_perms(perms.p, perms.num, permstr)) -@@ -1755,7 +1766,7 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - } - - /* We must own node to do this (tools can do this too). */ -- node = get_node_canonicalized(conn, in, in->buffer, &name, -+ node = get_node_canonicalized(conn, ctx, in->buffer, &name, - XS_PERM_WRITE | XS_PERM_OWNER); - if (!node) - return errno; -@@ -1790,7 +1801,7 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - return errno; - } - -- fire_watches(conn, in, name, node, false, &old_perms); -+ fire_watches(conn, ctx, name, node, false, &old_perms); - send_ack(conn, XS_SET_PERMS); - - return 0; -@@ -1798,7 +1809,8 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - - static struct { - const char *str; -- int (*func)(struct connection *conn, struct buffered_data *in); -+ int (*func)(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - unsigned int flags; - #define XS_FLAG_NOTID (1U << 0) /* Ignore transaction id. */ - #define XS_FLAG_PRIV (1U << 1) /* Privileged domain only. */ -@@ -1874,6 +1886,7 @@ static void process_message(struct connection *conn, struct buffered_data *in) - struct transaction *trans; - enum xsd_sockmsg_type type = in->hdr.msg.type; - int ret; -+ void *ctx; - - /* At least send_error() and send_reply() expects conn->in == in */ - assert(conn->in == in); -@@ -1898,10 +1911,17 @@ static void process_message(struct connection *conn, struct buffered_data *in) - return; - } - -+ ctx = talloc_new(NULL); -+ if (!ctx) { -+ send_error(conn, ENOMEM); -+ return; -+ } -+ - assert(conn->transaction == NULL); - conn->transaction = trans; - -- ret = wire_funcs[type].func(conn, in); -+ ret = wire_funcs[type].func(ctx, conn, in); -+ talloc_free(ctx); - if (ret) - send_error(conn, ret); - -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index e7c6886ccf47..fb732d0a14c3 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -330,7 +330,7 @@ bool domain_is_unprivileged(struct connection *conn) - domid_is_unprivileged(conn->domain->domid); - } - --static char *talloc_domain_path(void *context, unsigned int domid) -+static char *talloc_domain_path(const void *context, unsigned int domid) - { - return talloc_asprintf(context, "/local/domain/%u", domid); - } -@@ -534,7 +534,8 @@ static struct domain *introduce_domain(const void *ctx, - } - - /* domid, gfn, evtchn, path */ --int do_introduce(struct connection *conn, struct buffered_data *in) -+int do_introduce(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct domain *domain; - char *vec[3]; -@@ -552,7 +553,7 @@ int do_introduce(struct connection *conn, struct buffered_data *in) - if (port <= 0) - return EINVAL; - -- domain = introduce_domain(in, domid, port, false); -+ domain = introduce_domain(ctx, domid, port, false); - if (!domain) - return errno; - -@@ -575,7 +576,8 @@ static struct domain *find_connected_domain(unsigned int domid) - return domain; - } - --int do_set_target(struct connection *conn, struct buffered_data *in) -+int do_set_target(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - char *vec[2]; - unsigned int domid, tdomid; -@@ -619,7 +621,8 @@ static struct domain *onearg_domain(struct connection *conn, - } - - /* domid */ --int do_release(struct connection *conn, struct buffered_data *in) -+int do_release(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct domain *domain; - -@@ -634,7 +637,8 @@ int do_release(struct connection *conn, struct buffered_data *in) - return 0; - } - --int do_resume(struct connection *conn, struct buffered_data *in) -+int do_resume(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct domain *domain; - -@@ -649,7 +653,8 @@ int do_resume(struct connection *conn, struct buffered_data *in) - return 0; - } - --int do_get_domain_path(struct connection *conn, struct buffered_data *in) -+int do_get_domain_path(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - char *path; - const char *domid_str = onearg(in); -@@ -657,18 +662,17 @@ int do_get_domain_path(struct connection *conn, struct buffered_data *in) - if (!domid_str) - return EINVAL; - -- path = talloc_domain_path(conn, atoi(domid_str)); -+ path = talloc_domain_path(ctx, atoi(domid_str)); - if (!path) - return errno; - - send_reply(conn, XS_GET_DOMAIN_PATH, path, strlen(path) + 1); - -- talloc_free(path); -- - return 0; - } - --int do_is_domain_introduced(struct connection *conn, struct buffered_data *in) -+int do_is_domain_introduced(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - int result; - unsigned int domid; -@@ -689,7 +693,8 @@ int do_is_domain_introduced(struct connection *conn, struct buffered_data *in) - } - - /* Allow guest to reset all watches */ --int do_reset_watches(struct connection *conn, struct buffered_data *in) -+int do_reset_watches(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - conn_delete_all_watches(conn); - conn_delete_all_transactions(conn); -diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h -index 904faa923afb..b9e152890149 100644 ---- a/tools/xenstore/xenstored_domain.h -+++ b/tools/xenstore/xenstored_domain.h -@@ -24,25 +24,32 @@ void handle_event(void); - void check_domains(void); - - /* domid, mfn, eventchn, path */ --int do_introduce(struct connection *conn, struct buffered_data *in); -+int do_introduce(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - - /* domid */ --int do_is_domain_introduced(struct connection *conn, struct buffered_data *in); -+int do_is_domain_introduced(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - - /* domid */ --int do_release(struct connection *conn, struct buffered_data *in); -+int do_release(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - - /* domid */ --int do_resume(struct connection *conn, struct buffered_data *in); -+int do_resume(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - - /* domid, target */ --int do_set_target(struct connection *conn, struct buffered_data *in); -+int do_set_target(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - - /* domid */ --int do_get_domain_path(struct connection *conn, struct buffered_data *in); -+int do_get_domain_path(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - - /* Allow guest to reset all watches */ --int do_reset_watches(struct connection *conn, struct buffered_data *in); -+int do_reset_watches(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - - void domain_init(int evtfd); - void dom0_init(void); -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index 28774813de83..3e3eb47326cc 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -481,7 +481,8 @@ struct transaction *transaction_lookup(struct connection *conn, uint32_t id) - return ERR_PTR(-ENOENT); - } - --int do_transaction_start(struct connection *conn, struct buffered_data *in) -+int do_transaction_start(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct transaction *trans, *exists; - char id_str[20]; -@@ -494,8 +495,8 @@ int do_transaction_start(struct connection *conn, struct buffered_data *in) - conn->transaction_started > quota_max_transaction) - return ENOSPC; - -- /* Attach transaction to input for autofree until it's complete */ -- trans = talloc_zero(in, struct transaction); -+ /* Attach transaction to ctx for autofree until it's complete */ -+ trans = talloc_zero(ctx, struct transaction); - if (!trans) - return ENOMEM; - -@@ -544,7 +545,8 @@ static int transaction_fix_domains(struct transaction *trans, bool update) - return 0; - } - --int do_transaction_end(struct connection *conn, struct buffered_data *in) -+int do_transaction_end(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - const char *arg = onearg(in); - struct transaction *trans; -@@ -562,8 +564,8 @@ int do_transaction_end(struct connection *conn, struct buffered_data *in) - if (!conn->transaction_started) - conn->ta_start_time = 0; - -- /* Attach transaction to in for auto-cleanup */ -- talloc_steal(in, trans); -+ /* Attach transaction to ctx for auto-cleanup */ -+ talloc_steal(ctx, trans); - - if (streq(arg, "T")) { - if (trans->fail) -diff --git a/tools/xenstore/xenstored_transaction.h b/tools/xenstore/xenstored_transaction.h -index e3cbd6b23095..39d7f81c5127 100644 ---- a/tools/xenstore/xenstored_transaction.h -+++ b/tools/xenstore/xenstored_transaction.h -@@ -29,8 +29,10 @@ struct transaction; - - extern uint64_t generation; - --int do_transaction_start(struct connection *conn, struct buffered_data *node); --int do_transaction_end(struct connection *conn, struct buffered_data *in); -+int do_transaction_start(const void *ctx, struct connection *conn, -+ struct buffered_data *node); -+int do_transaction_end(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - - struct transaction *transaction_lookup(struct connection *conn, uint32_t id); - -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index 85362bcce314..316c08b7f754 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -243,7 +243,7 @@ static struct watch *add_watch(struct connection *conn, char *path, char *token, - return NULL; - } - --int do_watch(struct connection *conn, struct buffered_data *in) -+int do_watch(const void *ctx, struct connection *conn, struct buffered_data *in) - { - struct watch *watch; - char *vec[2]; -@@ -252,7 +252,7 @@ int do_watch(struct connection *conn, struct buffered_data *in) - if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) - return EINVAL; - -- errno = check_watch_path(conn, in, &(vec[0]), &relative); -+ errno = check_watch_path(conn, ctx, &(vec[0]), &relative); - if (errno) - return errno; - -@@ -283,7 +283,8 @@ int do_watch(struct connection *conn, struct buffered_data *in) - return 0; - } - --int do_unwatch(struct connection *conn, struct buffered_data *in) -+int do_unwatch(const void *ctx, struct connection *conn, -+ struct buffered_data *in) - { - struct watch *watch; - char *node, *vec[2]; -@@ -291,7 +292,7 @@ int do_unwatch(struct connection *conn, struct buffered_data *in) - if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) - return EINVAL; - -- node = xenstore_canonicalize(conn, in, vec[0]); -+ node = xenstore_canonicalize(conn, ctx, vec[0]); - if (!node) - return ENOMEM; - list_for_each_entry(watch, &conn->watches, list) { -diff --git a/tools/xenstore/xenstored_watch.h b/tools/xenstore/xenstored_watch.h -index 0e693f0839cd..091890edca96 100644 ---- a/tools/xenstore/xenstored_watch.h -+++ b/tools/xenstore/xenstored_watch.h -@@ -21,8 +21,10 @@ - - #include "xenstored_core.h" - --int do_watch(struct connection *conn, struct buffered_data *in); --int do_unwatch(struct connection *conn, struct buffered_data *in); -+int do_watch(const void *ctx, struct connection *conn, -+ struct buffered_data *in); -+int do_unwatch(const void *ctx, struct connection *conn, -+ struct buffered_data *in); - - /* Fire all watches: !exact means all the children are affected (ie. rm). */ - void fire_watches(struct connection *conn, const void *tmp, const char *name, diff --git a/xsa417.patch b/xsa417.patch deleted file mode 100644 index 3f10bf7..0000000 --- a/xsa417.patch +++ /dev/null @@ -1,135 +0,0 @@ -From 67d5ecd609b8f12346eadb40e547cd7e01d825dc Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:10 +0200 -Subject: tools/xenstore: fix checking node permissions - -Today chk_domain_generation() is being used to check whether a node -permission entry is still valid or whether it is referring to a domain -no longer existing. This is done by comparing the node's and the -domain's generation count. - -In case no struct domain is existing for a checked domain, but the -domain itself is valid, chk_domain_generation() assumes it is being -called due to the first node created for a new domain and it will -return success. - -This might be wrong in case the checked permission is related to an -old domain, which has just been replaced with a new domain using the -same domid. - -Fix that by letting chk_domain_generation() fail in case a struct -domain isn't found. In order to cover the case of the first node for -a new domain try to allocate the needed struct domain explicitly when -processing the related SET_PERMS command. In case a referenced domain -isn't existing, flag the related permission to be ignored right away. - -This is XSA-417 / CVE-2022-42320. - -Reported-by: Juergen Gross -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 66bbeaf6bfb0..a0c176fa203e 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1753,6 +1753,11 @@ static int do_set_perms(const void *ctx, struct connection *conn, - if (!xs_strings_to_perms(perms.p, perms.num, permstr)) - return errno; - -+ if (domain_alloc_permrefs(&perms) < 0) -+ return ENOMEM; -+ if (perms.p[0].perms & XS_PERM_IGNORE) -+ return ENOENT; -+ - /* First arg is node name. */ - if (strstarts(in->buffer, "@")) { - if (set_perms_special(conn, in->buffer, &perms)) -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index b9ff4ded8360..98b401fdec30 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -907,7 +907,6 @@ int domain_entry_inc(struct connection *conn, struct node *node) - * count (used for testing whether a node permission is older than a domain). - * - * Return values: -- * -1: error - * 0: domain has higher generation count (it is younger than a node with the - * given count), or domain isn't existing any longer - * 1: domain is older than the node -@@ -915,20 +914,38 @@ int domain_entry_inc(struct connection *conn, struct node *node) - static int chk_domain_generation(unsigned int domid, uint64_t gen) - { - struct domain *d; -- xc_dominfo_t dominfo; - - if (!xc_handle && domid == 0) - return 1; - - d = find_domain_struct(domid); -- if (d) -- return (d->generation <= gen) ? 1 : 0; - -- if (!get_domain_info(domid, &dominfo)) -- return 0; -+ return (d && d->generation <= gen) ? 1 : 0; -+} - -- d = alloc_domain(NULL, domid); -- return d ? 1 : -1; -+/* -+ * Allocate all missing struct domain referenced by a permission set. -+ * Any permission entries for not existing domains will be marked to be -+ * ignored. -+ */ -+int domain_alloc_permrefs(struct node_perms *perms) -+{ -+ unsigned int i, domid; -+ struct domain *d; -+ xc_dominfo_t dominfo; -+ -+ for (i = 0; i < perms->num; i++) { -+ domid = perms->p[i].id; -+ d = find_domain_struct(domid); -+ if (!d) { -+ if (!get_domain_info(domid, &dominfo)) -+ perms->p[i].perms |= XS_PERM_IGNORE; -+ else if (!alloc_domain(NULL, domid)) -+ return ENOMEM; -+ } -+ } -+ -+ return 0; - } - - /* -@@ -941,8 +958,6 @@ int domain_adjust_node_perms(struct connection *conn, struct node *node) - int ret; - - ret = chk_domain_generation(node->perms.p[0].id, node->generation); -- if (ret < 0) -- return errno; - - /* If the owner doesn't exist any longer give it to priv domain. */ - if (!ret) { -@@ -959,8 +974,6 @@ int domain_adjust_node_perms(struct connection *conn, struct node *node) - continue; - ret = chk_domain_generation(node->perms.p[i].id, - node->generation); -- if (ret < 0) -- return errno; - if (!ret) - node->perms.p[i].perms |= XS_PERM_IGNORE; - } -diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h -index 209442190911..7fe0a21d9e45 100644 ---- a/tools/xenstore/xenstored_domain.h -+++ b/tools/xenstore/xenstored_domain.h -@@ -63,6 +63,7 @@ bool domain_is_unprivileged(struct connection *conn); - - /* Remove node permissions for no longer existing domains. */ - int domain_adjust_node_perms(struct connection *conn, struct node *node); -+int domain_alloc_permrefs(struct node_perms *perms); - - /* Quota manipulation */ - int domain_entry_inc(struct connection *conn, struct node *); diff --git a/xsa418-4.16-01.patch b/xsa418-4.16-01.patch deleted file mode 100644 index c4a17b4..0000000 --- a/xsa418-4.16-01.patch +++ /dev/null @@ -1,119 +0,0 @@ -From d1e6dca486599ab914af7b38b3782b237d3d603b Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:11 +0200 -Subject: tools/xenstore: remove recursion from construct_node() - -In order to reduce stack usage due to recursion, switch -construct_node() to use a loop instead. - -This is part of XSA-418 / CVE-2022-42321. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index c676ee4e4e4f..3907c35643e9 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1377,45 +1377,69 @@ static int add_child(const void *ctx, struct node *parent, const char *name) - static struct node *construct_node(struct connection *conn, const void *ctx, - const char *name) - { -- struct node *parent, *node; -- char *parentname = get_parent(ctx, name); -+ const char **names = NULL; -+ unsigned int levels = 0; -+ struct node *node = NULL; -+ struct node *parent = NULL; -+ const char *parentname = talloc_strdup(ctx, name); - - if (!parentname) - return NULL; - -- /* If parent doesn't exist, create it. */ -- parent = read_node(conn, parentname, parentname); -- if (!parent && errno == ENOENT) -- parent = construct_node(conn, ctx, parentname); -- if (!parent) -- return NULL; -+ /* Walk the path up until an existing node is found. */ -+ while (!parent) { -+ names = talloc_realloc(ctx, names, const char *, levels + 1); -+ if (!names) -+ goto nomem; - -- /* Add child to parent. */ -- if (add_child(ctx, parent, name)) -- goto nomem; -+ /* -+ * names[0] is the name of the node to construct initially, -+ * names[1] is its parent, and so on. -+ */ -+ names[levels] = parentname; -+ parentname = get_parent(ctx, parentname); -+ if (!parentname) -+ return NULL; - -- /* Allocate node */ -- node = talloc(ctx, struct node); -- if (!node) -- goto nomem; -- node->name = talloc_strdup(node, name); -- if (!node->name) -- goto nomem; -+ /* Try to read parent node until we found an existing one. */ -+ parent = read_node(conn, ctx, parentname); -+ if (!parent && (errno != ENOENT || !strcmp(parentname, "/"))) -+ return NULL; - -- /* Inherit permissions, except unprivileged domains own what they create */ -- node->perms.num = parent->perms.num; -- node->perms.p = talloc_memdup(node, parent->perms.p, -- node->perms.num * sizeof(*node->perms.p)); -- if (!node->perms.p) -- goto nomem; -- if (domain_is_unprivileged(conn)) -- node->perms.p[0].id = conn->id; -+ levels++; -+ } -+ -+ /* Walk the path down again constructing the missing nodes. */ -+ for (; levels > 0; levels--) { -+ /* Add child to parent. */ -+ if (add_child(ctx, parent, names[levels - 1])) -+ goto nomem; -+ -+ /* Allocate node */ -+ node = talloc(ctx, struct node); -+ if (!node) -+ goto nomem; -+ node->name = talloc_steal(node, names[levels - 1]); -+ -+ /* Inherit permissions, unpriv domains own what they create. */ -+ node->perms.num = parent->perms.num; -+ node->perms.p = talloc_memdup(node, parent->perms.p, -+ node->perms.num * -+ sizeof(*node->perms.p)); -+ if (!node->perms.p) -+ goto nomem; -+ if (domain_is_unprivileged(conn)) -+ node->perms.p[0].id = conn->id; -+ -+ /* No children, no data */ -+ node->children = node->data = NULL; -+ node->childlen = node->datalen = 0; -+ node->acc.memory = 0; -+ node->parent = parent; -+ -+ parent = node; -+ } - -- /* No children, no data */ -- node->children = node->data = NULL; -- node->childlen = node->datalen = 0; -- node->acc.memory = 0; -- node->parent = parent; - return node; - - nomem: diff --git a/xsa418-4.16-02.patch b/xsa418-4.16-02.patch deleted file mode 100644 index 874bab2..0000000 --- a/xsa418-4.16-02.patch +++ /dev/null @@ -1,103 +0,0 @@ -From c13d85a2fe94bbf3cb8186b89324c5d1b4f9a61f Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:11 +0200 -Subject: tools/xenstore: don't let remove_child_entry() call corrupt() - -In case of write_node() returning an error, remove_child_entry() will -call corrupt() today. This could result in an endless recursion, as -remove_child_entry() is called by corrupt(), too: - -corrupt() - check_store() - check_store_() - remove_child_entry() - -Fix that by letting remove_child_entry() return an error instead and -let the caller decide what to do. - -This is part of XSA-418 / CVE-2022-42321. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 3907c35643e9..f433a45dc217 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1608,15 +1608,15 @@ static void memdel(void *mem, unsigned off, unsigned len, unsigned total) - memmove(mem + off, mem + off + len, total - off - len); - } - --static void remove_child_entry(struct connection *conn, struct node *node, -- size_t offset) -+static int remove_child_entry(struct connection *conn, struct node *node, -+ size_t offset) - { - size_t childlen = strlen(node->children + offset); - - memdel(node->children, offset, childlen + 1, node->childlen); - node->childlen -= childlen + 1; -- if (write_node(conn, node, true)) -- corrupt(conn, "Can't update parent node '%s'", node->name); -+ -+ return write_node(conn, node, true); - } - - static void delete_child(struct connection *conn, -@@ -1626,7 +1626,9 @@ static void delete_child(struct connection *conn, - - for (i = 0; i < node->childlen; i += strlen(node->children+i) + 1) { - if (streq(node->children+i, childname)) { -- remove_child_entry(conn, node, i); -+ if (remove_child_entry(conn, node, i)) -+ corrupt(conn, "Can't update parent node '%s'", -+ node->name); - return; - } - } -@@ -2325,6 +2327,17 @@ int remember_string(struct hashtable *hash, const char *str) - return hashtable_insert(hash, k, (void *)1); - } - -+static int rm_child_entry(struct node *node, size_t off, size_t len) -+{ -+ if (!recovery) -+ return off; -+ -+ if (remove_child_entry(NULL, node, off)) -+ log("check_store: child entry could not be removed from '%s'", -+ node->name); -+ -+ return off - len - 1; -+} - - /** - * A node has a children field that names the children of the node, separated -@@ -2377,12 +2390,7 @@ static int check_store_(const char *name, struct hashtable *reachable) - if (hashtable_search(children, childname)) { - log("check_store: '%s' is duplicated!", - childname); -- -- if (recovery) { -- remove_child_entry(NULL, node, -- i); -- i -= childlen + 1; -- } -+ i = rm_child_entry(node, i, childlen); - } - else { - if (!remember_string(children, -@@ -2399,11 +2407,7 @@ static int check_store_(const char *name, struct hashtable *reachable) - } else if (errno != ENOMEM) { - log("check_store: No child '%s' found!\n", - childname); -- -- if (recovery) { -- remove_child_entry(NULL, node, i); -- i -= childlen + 1; -- } -+ i = rm_child_entry(node, i, childlen); - } else { - log("check_store: ENOMEM"); - ret = ENOMEM; diff --git a/xsa418-4.16-03.patch b/xsa418-4.16-03.patch deleted file mode 100644 index 321ccf0..0000000 --- a/xsa418-4.16-03.patch +++ /dev/null @@ -1,243 +0,0 @@ -From aac9b51b6fbbbd16c910f69365345528c5bec106 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:11 +0200 -Subject: tools/xenstore: add generic treewalk function - -Add a generic function to walk the complete node tree. It will start -at "/" and descend recursively into each child, calling a function -specified by the caller. Depending on the return value of the user -specified function the walk will be aborted, continued, or the current -child will be skipped by not descending into its children. - -This is part of XSA-418 / CVE-2022-42321. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Acked-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index f433a45dc217..2cda3ee375ab 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1838,6 +1838,135 @@ static int do_set_perms(const void *ctx, struct connection *conn, - return 0; - } - -+static char *child_name(const void *ctx, const char *s1, const char *s2) -+{ -+ if (strcmp(s1, "/")) -+ return talloc_asprintf(ctx, "%s/%s", s1, s2); -+ return talloc_asprintf(ctx, "/%s", s2); -+} -+ -+static int rm_from_parent(struct connection *conn, struct node *parent, -+ const char *name) -+{ -+ size_t off; -+ -+ if (!parent) -+ return WALK_TREE_ERROR_STOP; -+ -+ for (off = parent->childoff - 1; off && parent->children[off - 1]; -+ off--); -+ if (remove_child_entry(conn, parent, off)) { -+ log("treewalk: child entry could not be removed from '%s'", -+ parent->name); -+ return WALK_TREE_ERROR_STOP; -+ } -+ parent->childoff = off; -+ -+ return WALK_TREE_OK; -+} -+ -+static int walk_call_func(const void *ctx, struct connection *conn, -+ struct node *node, struct node *parent, void *arg, -+ int (*func)(const void *ctx, struct connection *conn, -+ struct node *node, void *arg)) -+{ -+ int ret; -+ -+ if (!func) -+ return WALK_TREE_OK; -+ -+ ret = func(ctx, conn, node, arg); -+ if (ret == WALK_TREE_RM_CHILDENTRY && parent) -+ ret = rm_from_parent(conn, parent, node->name); -+ -+ return ret; -+} -+ -+int walk_node_tree(const void *ctx, struct connection *conn, const char *root, -+ struct walk_funcs *funcs, void *arg) -+{ -+ int ret = 0; -+ void *tmpctx; -+ char *name; -+ struct node *node = NULL; -+ struct node *parent = NULL; -+ -+ tmpctx = talloc_new(ctx); -+ if (!tmpctx) { -+ errno = ENOMEM; -+ return WALK_TREE_ERROR_STOP; -+ } -+ name = talloc_strdup(tmpctx, root); -+ if (!name) { -+ errno = ENOMEM; -+ talloc_free(tmpctx); -+ return WALK_TREE_ERROR_STOP; -+ } -+ -+ /* Continue the walk until an error is returned. */ -+ while (ret >= 0) { -+ /* node == NULL possible only for the initial loop iteration. */ -+ if (node) { -+ /* Go one step up if ret or if last child finished. */ -+ if (ret || node->childoff >= node->childlen) { -+ parent = node->parent; -+ /* Call function AFTER processing a node. */ -+ ret = walk_call_func(ctx, conn, node, parent, -+ arg, funcs->exit); -+ /* Last node, so exit loop. */ -+ if (!parent) -+ break; -+ talloc_free(node); -+ /* Continue with parent. */ -+ node = parent; -+ continue; -+ } -+ /* Get next child of current node. */ -+ name = child_name(tmpctx, node->name, -+ node->children + node->childoff); -+ if (!name) { -+ ret = WALK_TREE_ERROR_STOP; -+ break; -+ } -+ /* Point to next child. */ -+ node->childoff += strlen(node->children + -+ node->childoff) + 1; -+ /* Descent into children. */ -+ parent = node; -+ } -+ /* Read next node (root node or next child). */ -+ node = read_node(conn, tmpctx, name); -+ if (!node) { -+ /* Child not found - should not happen! */ -+ /* ENOENT case can be handled by supplied function. */ -+ if (errno == ENOENT && funcs->enoent) -+ ret = funcs->enoent(ctx, conn, parent, name, -+ arg); -+ else -+ ret = WALK_TREE_ERROR_STOP; -+ if (!parent) -+ break; -+ if (ret == WALK_TREE_RM_CHILDENTRY) -+ ret = rm_from_parent(conn, parent, name); -+ if (ret < 0) -+ break; -+ talloc_free(name); -+ node = parent; -+ continue; -+ } -+ talloc_free(name); -+ node->parent = parent; -+ node->childoff = 0; -+ /* Call function BEFORE processing a node. */ -+ ret = walk_call_func(ctx, conn, node, parent, arg, -+ funcs->enter); -+ } -+ -+ talloc_free(tmpctx); -+ -+ return ret < 0 ? ret : WALK_TREE_OK; -+} -+ - static struct { - const char *str; - int (*func)(const void *ctx, struct connection *conn, -@@ -2305,18 +2434,6 @@ static int keys_equal_fn(void *key1, void *key2) - return 0 == strcmp((char *)key1, (char *)key2); - } - -- --static char *child_name(const char *s1, const char *s2) --{ -- if (strcmp(s1, "/")) { -- return talloc_asprintf(NULL, "%s/%s", s1, s2); -- } -- else { -- return talloc_asprintf(NULL, "/%s", s2); -- } --} -- -- - int remember_string(struct hashtable *hash, const char *str) - { - char *k = malloc(strlen(str) + 1); -@@ -2376,7 +2493,7 @@ static int check_store_(const char *name, struct hashtable *reachable) - while (i < node->childlen && !ret) { - struct node *childnode; - size_t childlen = strlen(node->children + i); -- char * childname = child_name(node->name, -+ char * childname = child_name(NULL, node->name, - node->children + i); - - if (!childname) { -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index bfd3fc1e9df3..2d9942171d92 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -202,6 +202,7 @@ struct node { - - /* Children, each nul-terminated. */ - unsigned int childlen; -+ unsigned int childoff; /* Used by walk_node_tree() internally. */ - char *children; - - /* Allocation information for node currently in store. */ -@@ -338,6 +339,45 @@ void read_state_buffered_data(const void *ctx, struct connection *conn, - const struct xs_state_connection *sc); - void read_state_node(const void *ctx, const void *state); - -+/* -+ * Walk the node tree below root calling funcs->enter() and funcs->exit() for -+ * each node. funcs->enter() is being called when entering a node, so before -+ * any of the children of the node is processed. funcs->exit() is being -+ * called when leaving the node, so after all children have been processed. -+ * funcs->enoent() is being called when a node isn't existing. -+ * funcs->*() return values: -+ * < 0: tree walk is stopped, walk_node_tree() returns funcs->*() return value -+ * in case WALK_TREE_ERROR_STOP is returned, errno should be set -+ * WALK_TREE_OK: tree walk is continuing -+ * WALK_TREE_SKIP_CHILDREN: tree walk won't descend below current node, but -+ * walk continues -+ * WALK_TREE_RM_CHILDENTRY: Remove the child entry from its parent and write -+ * the modified parent node back to the data base, implies to not descend -+ * below the current node, but to continue the walk -+ * funcs->*() is allowed to modify the node it is called for in the data base. -+ * In case funcs->enter() is deleting the node, it must not return WALK_TREE_OK -+ * in order to avoid descending into no longer existing children. -+ */ -+/* Return values for funcs->*() and walk_node_tree(). */ -+#define WALK_TREE_SUCCESS_STOP -100 /* Stop walk early, no error. */ -+#define WALK_TREE_ERROR_STOP -1 /* Stop walk due to error. */ -+#define WALK_TREE_OK 0 /* No error. */ -+/* Return value for funcs->*() only. */ -+#define WALK_TREE_SKIP_CHILDREN 1 /* Don't recurse below current node. */ -+#define WALK_TREE_RM_CHILDENTRY 2 /* Remove child entry from parent. */ -+ -+struct walk_funcs { -+ int (*enter)(const void *ctx, struct connection *conn, -+ struct node *node, void *arg); -+ int (*exit)(const void *ctx, struct connection *conn, -+ struct node *node, void *arg); -+ int (*enoent)(const void *ctx, struct connection *conn, -+ struct node *parent, char *name, void *arg); -+}; -+ -+int walk_node_tree(const void *ctx, struct connection *conn, const char *root, -+ struct walk_funcs *funcs, void *arg); -+ - #endif /* _XENSTORED_CORE_H */ - - /* diff --git a/xsa418-4.16-04.patch b/xsa418-4.16-04.patch deleted file mode 100644 index 95de88f..0000000 --- a/xsa418-4.16-04.patch +++ /dev/null @@ -1,108 +0,0 @@ -From bdc931fb5dcebbd8d0e44b5d8bd3fb9106ee8596 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:12 +0200 -Subject: tools/xenstore: simplify check_store() - -check_store() is using a hash table for storing all node names it has -found via walking the tree. Additionally it using another hash table -for all children of a node to detect duplicate child names. - -Simplify that by dropping the second hash table as the first one is -already holding all the needed information. - -This is part of XSA-418 / CVE-2022-42321. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 2cda3ee375ab..760f3c16c794 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -2477,50 +2477,34 @@ static int check_store_(const char *name, struct hashtable *reachable) - if (node) { - size_t i = 0; - -- struct hashtable * children = -- create_hashtable(16, hash_from_key_fn, keys_equal_fn); -- if (!children) { -- log("check_store create table: ENOMEM"); -- return ENOMEM; -- } -- - if (!remember_string(reachable, name)) { -- hashtable_destroy(children, 0); - log("check_store: ENOMEM"); - return ENOMEM; - } - - while (i < node->childlen && !ret) { -- struct node *childnode; -+ struct node *childnode = NULL; - size_t childlen = strlen(node->children + i); -- char * childname = child_name(NULL, node->name, -- node->children + i); -+ char *childname = child_name(NULL, node->name, -+ node->children + i); - - if (!childname) { - log("check_store: ENOMEM"); - ret = ENOMEM; - break; - } -+ -+ if (hashtable_search(reachable, childname)) { -+ log("check_store: '%s' is duplicated!", -+ childname); -+ i = rm_child_entry(node, i, childlen); -+ goto next; -+ } -+ - childnode = read_node(NULL, childname, childname); -- -+ - if (childnode) { -- if (hashtable_search(children, childname)) { -- log("check_store: '%s' is duplicated!", -- childname); -- i = rm_child_entry(node, i, childlen); -- } -- else { -- if (!remember_string(children, -- childname)) { -- log("check_store: ENOMEM"); -- talloc_free(childnode); -- talloc_free(childname); -- ret = ENOMEM; -- break; -- } -- ret = check_store_(childname, -- reachable); -- } -+ ret = check_store_(childname, reachable); - } else if (errno != ENOMEM) { - log("check_store: No child '%s' found!\n", - childname); -@@ -2530,19 +2514,18 @@ static int check_store_(const char *name, struct hashtable *reachable) - ret = ENOMEM; - } - -+ next: - talloc_free(childnode); - talloc_free(childname); - i += childlen + 1; - } - -- hashtable_destroy(children, 0 /* Don't free values (they are -- all (void *)1) */); - talloc_free(node); - } else if (errno != ENOMEM) { - /* Impossible, because no database should ever be without the - root, and otherwise, we've just checked in our caller - (which made a recursive call to get here). */ -- -+ - log("check_store: No child '%s' found: impossible!", name); - } else { - log("check_store: ENOMEM"); diff --git a/xsa418-4.16-05.patch b/xsa418-4.16-05.patch deleted file mode 100644 index fca551e..0000000 --- a/xsa418-4.16-05.patch +++ /dev/null @@ -1,164 +0,0 @@ -From 27817f0a7d6802be04e8f43a0900b02f881b28b2 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:12 +0200 -Subject: tools/xenstore: use treewalk for check_store() - -Instead of doing an open tree walk using call recursion, use -walk_node_tree() when checking the store for inconsistencies. - -This will reduce code size and avoid many nesting levels of function -calls which could potentially exhaust the stack. - -This is part of XSA-418 / CVE-2022-42321. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 760f3c16c794..efdd1888fd78 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -2444,18 +2444,6 @@ int remember_string(struct hashtable *hash, const char *str) - return hashtable_insert(hash, k, (void *)1); - } - --static int rm_child_entry(struct node *node, size_t off, size_t len) --{ -- if (!recovery) -- return off; -- -- if (remove_child_entry(NULL, node, off)) -- log("check_store: child entry could not be removed from '%s'", -- node->name); -- -- return off - len - 1; --} -- - /** - * A node has a children field that names the children of the node, separated - * by NULs. We check whether there are entries in there that are duplicated -@@ -2469,70 +2457,29 @@ static int rm_child_entry(struct node *node, size_t off, size_t len) - * As we go, we record each node in the given reachable hashtable. These - * entries will be used later in clean_store. - */ --static int check_store_(const char *name, struct hashtable *reachable) -+static int check_store_step(const void *ctx, struct connection *conn, -+ struct node *node, void *arg) - { -- struct node *node = read_node(NULL, name, name); -- int ret = 0; -- -- if (node) { -- size_t i = 0; -- -- if (!remember_string(reachable, name)) { -- log("check_store: ENOMEM"); -- return ENOMEM; -- } -- -- while (i < node->childlen && !ret) { -- struct node *childnode = NULL; -- size_t childlen = strlen(node->children + i); -- char *childname = child_name(NULL, node->name, -- node->children + i); -- -- if (!childname) { -- log("check_store: ENOMEM"); -- ret = ENOMEM; -- break; -- } -+ struct hashtable *reachable = arg; - -- if (hashtable_search(reachable, childname)) { -- log("check_store: '%s' is duplicated!", -- childname); -- i = rm_child_entry(node, i, childlen); -- goto next; -- } -- -- childnode = read_node(NULL, childname, childname); -- -- if (childnode) { -- ret = check_store_(childname, reachable); -- } else if (errno != ENOMEM) { -- log("check_store: No child '%s' found!\n", -- childname); -- i = rm_child_entry(node, i, childlen); -- } else { -- log("check_store: ENOMEM"); -- ret = ENOMEM; -- } -+ if (hashtable_search(reachable, (void *)node->name)) { -+ log("check_store: '%s' is duplicated!", node->name); -+ return recovery ? WALK_TREE_RM_CHILDENTRY -+ : WALK_TREE_SKIP_CHILDREN; -+ } - -- next: -- talloc_free(childnode); -- talloc_free(childname); -- i += childlen + 1; -- } -+ if (!remember_string(reachable, node->name)) -+ return WALK_TREE_ERROR_STOP; - -- talloc_free(node); -- } else if (errno != ENOMEM) { -- /* Impossible, because no database should ever be without the -- root, and otherwise, we've just checked in our caller -- (which made a recursive call to get here). */ -+ return WALK_TREE_OK; -+} - -- log("check_store: No child '%s' found: impossible!", name); -- } else { -- log("check_store: ENOMEM"); -- ret = ENOMEM; -- } -+static int check_store_enoent(const void *ctx, struct connection *conn, -+ struct node *parent, char *name, void *arg) -+{ -+ log("check_store: node '%s' not found", name); - -- return ret; -+ return recovery ? WALK_TREE_RM_CHILDENTRY : WALK_TREE_OK; - } - - -@@ -2581,24 +2528,28 @@ static void clean_store(struct hashtable *reachable) - - void check_store(void) - { -- char * root = talloc_strdup(NULL, "/"); -- struct hashtable * reachable = -- create_hashtable(16, hash_from_key_fn, keys_equal_fn); -- -+ struct hashtable *reachable; -+ struct walk_funcs walkfuncs = { -+ .enter = check_store_step, -+ .enoent = check_store_enoent, -+ }; -+ -+ reachable = create_hashtable(16, hash_from_key_fn, keys_equal_fn); - if (!reachable) { - log("check_store: ENOMEM"); - return; - } - - log("Checking store ..."); -- if (!check_store_(root, reachable) && -- !check_transactions(reachable)) -+ if (walk_node_tree(NULL, NULL, "/", &walkfuncs, reachable)) { -+ if (errno == ENOMEM) -+ log("check_store: ENOMEM"); -+ } else if (!check_transactions(reachable)) - clean_store(reachable); - log("Checking store complete."); - - hashtable_destroy(reachable, 0 /* Don't free values (they are all - (void *)1) */); -- talloc_free(root); - } - - diff --git a/xsa418-4.16-06.patch b/xsa418-4.16-06.patch deleted file mode 100644 index d46c057..0000000 --- a/xsa418-4.16-06.patch +++ /dev/null @@ -1,174 +0,0 @@ -From 6ea0ffbd88b11f23779d763501ec1370b590bb2a Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:12 +0200 -Subject: tools/xenstore: use treewalk for deleting nodes - -Instead of doing an open tree walk using call recursion, use -walk_node_tree() when deleting a sub-tree of nodes. - -This will reduce code size and avoid many nesting levels of function -calls which could potentially exhaust the stack. - -This is part of XSA-418 / CVE-2022-42321. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Acked-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index efdd1888fd78..58fb651542ec 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1334,21 +1334,6 @@ static int do_read(const void *ctx, struct connection *conn, - return 0; - } - --static void delete_node_single(struct connection *conn, struct node *node) --{ -- TDB_DATA key; -- -- if (access_node(conn, node, NODE_ACCESS_DELETE, &key)) -- return; -- -- if (do_tdb_delete(conn, &key, &node->acc) != 0) { -- corrupt(conn, "Could not delete '%s'", node->name); -- return; -- } -- -- domain_entry_dec(conn, node); --} -- - /* Must not be / */ - static char *basename(const char *name) - { -@@ -1619,69 +1604,59 @@ static int remove_child_entry(struct connection *conn, struct node *node, - return write_node(conn, node, true); - } - --static void delete_child(struct connection *conn, -- struct node *node, const char *childname) -+static int delete_child(struct connection *conn, -+ struct node *node, const char *childname) - { - unsigned int i; - - for (i = 0; i < node->childlen; i += strlen(node->children+i) + 1) { - if (streq(node->children+i, childname)) { -- if (remove_child_entry(conn, node, i)) -- corrupt(conn, "Can't update parent node '%s'", -- node->name); -- return; -+ errno = remove_child_entry(conn, node, i) ? EIO : 0; -+ return errno; - } - } - corrupt(conn, "Can't find child '%s' in %s", childname, node->name); -+ -+ errno = EIO; -+ return errno; - } - --static int delete_node(struct connection *conn, const void *ctx, -- struct node *parent, struct node *node, bool watch_exact) -+static int delnode_sub(const void *ctx, struct connection *conn, -+ struct node *node, void *arg) - { -- char *name; -+ const char *root = arg; -+ bool watch_exact; -+ int ret; -+ TDB_DATA key; - -- /* Delete children. */ -- while (node->childlen) { -- struct node *child; -+ /* Any error here will probably be repeated for all following calls. */ -+ ret = access_node(conn, node, NODE_ACCESS_DELETE, &key); -+ if (ret > 0) -+ return WALK_TREE_SUCCESS_STOP; - -- name = talloc_asprintf(node, "%s/%s", node->name, -- node->children); -- child = name ? read_node(conn, node, name) : NULL; -- if (child) { -- if (delete_node(conn, ctx, node, child, true)) -- return errno; -- } else { -- trace("delete_node: Error deleting child '%s/%s'!\n", -- node->name, node->children); -- /* Quit deleting. */ -- errno = ENOMEM; -- return errno; -- } -- talloc_free(name); -- } -+ /* In case of error stop the walk. */ -+ if (!ret && do_tdb_delete(conn, &key, &node->acc)) -+ return WALK_TREE_SUCCESS_STOP; - - /* - * Fire the watches now, when we can still see the node permissions. - * This fine as we are single threaded and the next possible read will - * be handled only after the node has been really removed. -- */ -+ */ -+ watch_exact = strcmp(root, node->name); - fire_watches(conn, ctx, node->name, node, watch_exact, NULL); -- delete_node_single(conn, node); -- delete_child(conn, parent, basename(node->name)); -- talloc_free(node); - -- return 0; -+ domain_entry_dec(conn, node); -+ -+ return WALK_TREE_RM_CHILDENTRY; - } - --static int _rm(struct connection *conn, const void *ctx, struct node *node, -- const char *name) -+static int _rm(struct connection *conn, const void *ctx, const char *name) - { -- /* -- * Deleting node by node, so the result is always consistent even in -- * case of a failure. -- */ - struct node *parent; - char *parentname = get_parent(ctx, name); -+ struct walk_funcs walkfuncs = { .exit = delnode_sub }; -+ int ret; - - if (!parentname) - return errno; -@@ -1689,9 +1664,21 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, - parent = read_node(conn, ctx, parentname); - if (!parent) - return read_node_can_propagate_errno() ? errno : EINVAL; -- node->parent = parent; - -- return delete_node(conn, ctx, parent, node, false); -+ ret = walk_node_tree(ctx, conn, name, &walkfuncs, (void *)name); -+ if (ret < 0) { -+ if (ret == WALK_TREE_ERROR_STOP) { -+ corrupt(conn, "error when deleting sub-nodes of %s\n", -+ name); -+ errno = EIO; -+ } -+ return errno; -+ } -+ -+ if (delete_child(conn, parent, basename(name))) -+ return errno; -+ -+ return 0; - } - - -@@ -1728,7 +1715,7 @@ static int do_rm(const void *ctx, struct connection *conn, - if (streq(name, "/")) - return EINVAL; - -- ret = _rm(conn, ctx, node, name); -+ ret = _rm(conn, ctx, name); - if (ret) - return ret; - diff --git a/xsa418-4.16-07.patch b/xsa418-4.16-07.patch deleted file mode 100644 index 9600d8c..0000000 --- a/xsa418-4.16-07.patch +++ /dev/null @@ -1,163 +0,0 @@ -From 1ee281b18b52bec87335ea64ee74cc159e63d036 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:12 +0200 -Subject: tools/xenstore: use treewalk for creating node records - -Instead of doing an open tree walk using call recursion, use -walk_node_tree() when creating the node records during a live update. - -This will reduce code size and avoid many nesting levels of function -calls which could potentially exhaust the stack. - -This is part of XSA-418 / CVE-2022-42321. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 58fb651542ec..05d349778bb4 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -3120,101 +3120,76 @@ const char *dump_state_node_perms(FILE *fp, const struct xs_permissions *perms, - return NULL; - } - --static const char *dump_state_node_tree(FILE *fp, char *path, -- unsigned int path_max_len) -+struct dump_node_data { -+ FILE *fp; -+ const char *err; -+}; -+ -+static int dump_state_node_err(struct dump_node_data *data, const char *err) - { -- unsigned int pathlen, childlen, p = 0; -+ data->err = err; -+ return WALK_TREE_ERROR_STOP; -+} -+ -+static int dump_state_node(const void *ctx, struct connection *conn, -+ struct node *node, void *arg) -+{ -+ struct dump_node_data *data = arg; -+ FILE *fp = data->fp; -+ unsigned int pathlen; - struct xs_state_record_header head; - struct xs_state_node sn; -- TDB_DATA key, data; -- const struct xs_tdb_record_hdr *hdr; -- const char *child; - const char *ret; - -- pathlen = strlen(path) + 1; -- -- set_tdb_key(path, &key); -- data = tdb_fetch(tdb_ctx, key); -- if (data.dptr == NULL) -- return "Error reading node"; -- -- /* Clean up in case of failure. */ -- talloc_steal(path, data.dptr); -- -- hdr = (void *)data.dptr; -+ pathlen = strlen(node->name) + 1; - - head.type = XS_STATE_TYPE_NODE; - head.length = sizeof(sn); - sn.conn_id = 0; - sn.ta_id = 0; - sn.ta_access = 0; -- sn.perm_n = hdr->num_perms; -+ sn.perm_n = node->perms.num; - sn.path_len = pathlen; -- sn.data_len = hdr->datalen; -- head.length += hdr->num_perms * sizeof(*sn.perms); -+ sn.data_len = node->datalen; -+ head.length += node->perms.num * sizeof(*sn.perms); - head.length += pathlen; -- head.length += hdr->datalen; -+ head.length += node->datalen; - head.length = ROUNDUP(head.length, 3); - - if (fwrite(&head, sizeof(head), 1, fp) != 1) -- return "Dump node state error"; -+ return dump_state_node_err(data, "Dump node head error"); - if (fwrite(&sn, sizeof(sn), 1, fp) != 1) -- return "Dump node state error"; -+ return dump_state_node_err(data, "Dump node state error"); - -- ret = dump_state_node_perms(fp, hdr->perms, hdr->num_perms); -+ ret = dump_state_node_perms(fp, node->perms.p, node->perms.num); - if (ret) -- return ret; -+ return dump_state_node_err(data, ret); -+ -+ if (fwrite(node->name, pathlen, 1, fp) != 1) -+ return dump_state_node_err(data, "Dump node path error"); - -- if (fwrite(path, pathlen, 1, fp) != 1) -- return "Dump node path error"; -- if (hdr->datalen && -- fwrite(hdr->perms + hdr->num_perms, hdr->datalen, 1, fp) != 1) -- return "Dump node data error"; -+ if (node->datalen && fwrite(node->data, node->datalen, 1, fp) != 1) -+ return dump_state_node_err(data, "Dump node data error"); - - ret = dump_state_align(fp); - if (ret) -- return ret; -+ return dump_state_node_err(data, ret); - -- child = (char *)(hdr->perms + hdr->num_perms) + hdr->datalen; -- -- /* -- * Use path for constructing children paths. -- * As we don't write out nodes without having written their parent -- * already we will never clobber a part of the path we'll need later. -- */ -- pathlen--; -- if (path[pathlen - 1] != '/') { -- path[pathlen] = '/'; -- pathlen++; -- } -- while (p < hdr->childlen) { -- childlen = strlen(child) + 1; -- if (pathlen + childlen > path_max_len) -- return "Dump node path length error"; -- strcpy(path + pathlen, child); -- ret = dump_state_node_tree(fp, path, path_max_len); -- if (ret) -- return ret; -- p += childlen; -- child += childlen; -- } -- -- talloc_free(data.dptr); -- -- return NULL; -+ return WALK_TREE_OK; - } - - const char *dump_state_nodes(FILE *fp, const void *ctx) - { -- char *path; -- -- path = talloc_size(ctx, XENSTORE_ABS_PATH_MAX + 1); -- if (!path) -- return "Path buffer allocation error"; -+ struct dump_node_data data = { -+ .fp = fp, -+ .err = "Dump node walk error" -+ }; -+ struct walk_funcs walkfuncs = { .enter = dump_state_node }; - -- strcpy(path, "/"); -+ if (walk_node_tree(ctx, NULL, "/", &walkfuncs, &data)) -+ return data.err; - -- return dump_state_node_tree(fp, path, XENSTORE_ABS_PATH_MAX + 1); -+ return NULL; - } - - void read_state_global(const void *ctx, const void *state) diff --git a/xsa419-oxenstored.patch b/xsa419-oxenstored.patch deleted file mode 100644 index 0ac365d..0000000 --- a/xsa419-oxenstored.patch +++ /dev/null @@ -1,85 +0,0 @@ -From 09228369a549427294febe351372d7227e624da1 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Date: Wed, 12 Oct 2022 19:13:06 +0100 -Subject: tools/ocaml/xenstored: Fix quota bypass on domain shutdown -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -XSA-322 fixed a domid reuse vulnerability by assigning Dom0 as the owner of -any nodes left after a domain is shutdown (e.g. outside its /local/domain/N -tree). - -However Dom0 has no quota on purpose, so this opened up another potential -attack vector. Avoid it by deleting these nodes instead of assigning them to -Dom0. - -This is part of XSA-419 / CVE-2022-42323. - -Reported-by: Juergen Gross -Fixes: c46eff921209 ("tools/ocaml/xenstored: clean up permissions for dead domains") -Signed-off-by: Edwin Török -Acked-by: Christian Lindig - -diff --git a/tools/ocaml/xenstored/perms.ml b/tools/ocaml/xenstored/perms.ml -index e8a16221f8fa..84f2503e8e29 100644 ---- a/tools/ocaml/xenstored/perms.ml -+++ b/tools/ocaml/xenstored/perms.ml -@@ -64,8 +64,7 @@ let get_owner perm = perm.owner - * *) - let remove_domid ~domid perm = - let acl = List.filter (fun (acl_domid, _) -> acl_domid <> domid) perm.acl in -- let owner = if perm.owner = domid then 0 else perm.owner in -- { perm with acl; owner } -+ if perm.owner = domid then None else Some { perm with acl; owner = perm.owner } - - let default0 = create 0 NONE [] - -diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml -index 20e67b142746..70f0c83de404 100644 ---- a/tools/ocaml/xenstored/store.ml -+++ b/tools/ocaml/xenstored/store.ml -@@ -87,10 +87,21 @@ let check_owner node connection = - - let rec recurse fct node = fct node; SymbolMap.iter (fun _ -> recurse fct) node.children - --(** [recurse_map f tree] applies [f] on each node in the tree recursively *) --let recurse_map f = -+(** [recurse_filter_map f tree] applies [f] on each node in the tree recursively, -+ possibly removing some nodes. -+ Note that the nodes removed this way won't generate watch events. -+*) -+let recurse_filter_map f = -+ let invalid = -1 in -+ let is_valid _ node = node.perms.owner <> invalid in - let rec walk node = -- f { node with children = SymbolMap.map walk node.children } -+ (* Map.filter_map is Ocaml 4.11+ only *) -+ let node = -+ { node with children = -+ SymbolMap.map walk node.children |> SymbolMap.filter is_valid } in -+ match f node with -+ | Some keep -> keep -+ | None -> { node with perms = {node.perms with owner = invalid } } - in - walk - -@@ -444,11 +455,13 @@ let setperms store perm path nperms = - - let reset_permissions store domid = - Logging.info "store|node" "Cleaning up xenstore ACLs for domid %d" domid; -- store.root <- Node.recurse_map (fun node -> -- let perms = Perms.Node.remove_domid ~domid node.perms in -- if perms <> node.perms then -- Logging.debug "store|node" "Changed permissions for node %s" (Node.get_name node); -- { node with perms } -+ store.root <- Node.recurse_filter_map (fun node -> -+ match Perms.Node.remove_domid ~domid node.perms with -+ | None -> None -+ | Some perms -> -+ if perms <> node.perms then -+ Logging.debug "store|node" "Changed permissions for node %s" (Node.get_name node); -+ Some { node with perms } - ) store.root - - type ops = { diff --git a/xsa419-xenstored-01.patch b/xsa419-xenstored-01.patch deleted file mode 100644 index 3409790..0000000 --- a/xsa419-xenstored-01.patch +++ /dev/null @@ -1,289 +0,0 @@ -From e4250bf8b39ed73623c75b0f1436ac7c1d45aba7 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:12 +0200 -Subject: tools/xenstore: remove nodes owned by destroyed domain - -In case a domain is removed from Xenstore, remove all nodes owned by -it per default. - -This tackles the problem that nodes might be created by a domain -outside its home path in Xenstore, leading to Xenstore hogging more -and more memory. Domain quota don't work in this case if the guest is -rebooting in between. - -Since XSA-322 ownership of such stale nodes is transferred to dom0, -which is helping against unintended access, but not against OOM of -Xenstore. - -As a fallback for weird cases add a Xenstore start parameter for -keeping today's way to handle stale nodes, adding the risk of Xenstore -hitting an OOM situation. - -This is part of XSA-419 / CVE-2022-42322. - -Reported-by: Juergen Gross -Fixes: 496306324d8d ("tools/xenstore: revoke access rights for removed domains") -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index bdc14679adf5..13e48aaa731c 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -80,6 +80,7 @@ static bool verbose = false; - LIST_HEAD(connections); - int tracefd = -1; - static bool recovery = true; -+bool keep_orphans = false; - static int reopen_log_pipe[2]; - static int reopen_log_pipe0_pollfd_idx = -1; - char *tracefile = NULL; -@@ -753,7 +754,7 @@ struct node *read_node(struct connection *conn, const void *ctx, - node->perms.p = hdr->perms; - node->acc.domid = node->perms.p[0].id; - node->acc.memory = data.dsize; -- if (domain_adjust_node_perms(conn, node)) -+ if (domain_adjust_node_perms(node)) - goto error; - - /* If owner is gone reset currently accounted memory size. */ -@@ -796,7 +797,7 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, - void *p; - struct xs_tdb_record_hdr *hdr; - -- if (domain_adjust_node_perms(conn, node)) -+ if (domain_adjust_node_perms(node)) - return errno; - - data.dsize = sizeof(*hdr) -@@ -1647,7 +1648,7 @@ static int delnode_sub(const void *ctx, struct connection *conn, - return WALK_TREE_RM_CHILDENTRY; - } - --static int _rm(struct connection *conn, const void *ctx, const char *name) -+int rm_node(struct connection *conn, const void *ctx, const char *name) - { - struct node *parent; - char *parentname = get_parent(ctx, name); -@@ -1711,7 +1712,7 @@ static int do_rm(const void *ctx, struct connection *conn, - if (streq(name, "/")) - return EINVAL; - -- ret = _rm(conn, ctx, name); -+ ret = rm_node(conn, ctx, name); - if (ret) - return ret; - -@@ -2618,6 +2619,8 @@ static void usage(void) - " -R, --no-recovery to request that no recovery should be attempted when\n" - " the store is corrupted (debug only),\n" - " -I, --internal-db store database in memory, not on disk\n" -+" -K, --keep-orphans don't delete nodes owned by a domain when the\n" -+" domain is deleted (this is a security risk!)\n" - " -V, --verbose to request verbose execution.\n"); - } - -@@ -2642,6 +2645,7 @@ static struct option options[] = { - { "timeout", 1, NULL, 'w' }, - { "no-recovery", 0, NULL, 'R' }, - { "internal-db", 0, NULL, 'I' }, -+ { "keep-orphans", 0, NULL, 'K' }, - { "verbose", 0, NULL, 'V' }, - { "watch-nb", 1, NULL, 'W' }, - #ifndef NO_LIVE_UPDATE -@@ -2721,7 +2725,7 @@ int main(int argc, char *argv[]) - orig_argc = argc; - orig_argv = argv; - -- while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:Q:q:T:RVW:w:U", -+ while ((opt = getopt_long(argc, argv, "DE:F:HKNPS:t:A:M:Q:q:T:RVW:w:U", - options, NULL)) != -1) { - switch (opt) { - case 'D': -@@ -2757,6 +2761,9 @@ int main(int argc, char *argv[]) - case 'I': - tdb_flags = TDB_INTERNAL|TDB_NOLOCK; - break; -+ case 'K': -+ keep_orphans = true; -+ break; - case 'V': - verbose = true; - break; -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index acb00ad96914..37006d508dbf 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -240,6 +240,9 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, - struct node *read_node(struct connection *conn, const void *ctx, - const char *name); - -+/* Remove a node and its children. */ -+int rm_node(struct connection *conn, const void *ctx, const char *name); -+ - void setup_structure(bool live_update); - struct connection *new_connection(const struct interface_funcs *funcs); - struct connection *get_connection_by_id(unsigned int conn_id); -@@ -284,6 +287,7 @@ extern int quota_req_outstanding; - extern int quota_trans_nodes; - extern int quota_memory_per_domain_soft; - extern int quota_memory_per_domain_hard; -+extern bool keep_orphans; - - extern unsigned int timeout_watch_event_msec; - -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index 98b401fdec30..84b7817cd5e6 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -227,10 +227,64 @@ static void unmap_interface(void *interface) - xengnttab_unmap(*xgt_handle, interface, 1); - } - -+static int domain_tree_remove_sub(const void *ctx, struct connection *conn, -+ struct node *node, void *arg) -+{ -+ struct domain *domain = arg; -+ TDB_DATA key; -+ int ret = WALK_TREE_OK; -+ -+ if (node->perms.p[0].id != domain->domid) -+ return WALK_TREE_OK; -+ -+ if (keep_orphans) { -+ set_tdb_key(node->name, &key); -+ domain->nbentry--; -+ node->perms.p[0].id = priv_domid; -+ node->acc.memory = 0; -+ domain_entry_inc(NULL, node); -+ if (write_node_raw(NULL, &key, node, true)) { -+ /* That's unfortunate. We only can try to continue. */ -+ syslog(LOG_ERR, -+ "error when moving orphaned node %s to dom0\n", -+ node->name); -+ } else -+ trace("orphaned node %s moved to dom0\n", node->name); -+ } else { -+ if (rm_node(NULL, ctx, node->name)) { -+ /* That's unfortunate. We only can try to continue. */ -+ syslog(LOG_ERR, -+ "error when deleting orphaned node %s\n", -+ node->name); -+ } else -+ trace("orphaned node %s deleted\n", node->name); -+ -+ /* Skip children in all cases in order to avoid more errors. */ -+ ret = WALK_TREE_SKIP_CHILDREN; -+ } -+ -+ return domain->nbentry > 0 ? ret : WALK_TREE_SUCCESS_STOP; -+} -+ -+static void domain_tree_remove(struct domain *domain) -+{ -+ int ret; -+ struct walk_funcs walkfuncs = { .enter = domain_tree_remove_sub }; -+ -+ if (domain->nbentry > 0) { -+ ret = walk_node_tree(domain, NULL, "/", &walkfuncs, domain); -+ if (ret == WALK_TREE_ERROR_STOP) -+ syslog(LOG_ERR, -+ "error when looking for orphaned nodes\n"); -+ } -+} -+ - static int destroy_domain(void *_domain) - { - struct domain *domain = _domain; - -+ domain_tree_remove(domain); -+ - list_del(&domain->list); - - if (!domain->introduced) -@@ -883,15 +937,15 @@ int domain_entry_inc(struct connection *conn, struct node *node) - struct domain *d; - unsigned int domid; - -- if (!conn) -+ if (!node->perms.p) - return 0; - -- domid = node->perms.p ? node->perms.p[0].id : conn->id; -+ domid = node->perms.p[0].id; - -- if (conn->transaction) { -+ if (conn && conn->transaction) { - transaction_entry_inc(conn->transaction, domid); - } else { -- d = (domid == conn->id && conn->domain) ? conn->domain -+ d = (conn && domid == conn->id && conn->domain) ? conn->domain - : find_or_alloc_existing_domain(domid); - if (d) - d->nbentry++; -@@ -952,23 +1006,11 @@ int domain_alloc_permrefs(struct node_perms *perms) - * Remove permissions for no longer existing domains in order to avoid a new - * domain with the same domid inheriting the permissions. - */ --int domain_adjust_node_perms(struct connection *conn, struct node *node) -+int domain_adjust_node_perms(struct node *node) - { - unsigned int i; - int ret; - -- ret = chk_domain_generation(node->perms.p[0].id, node->generation); -- -- /* If the owner doesn't exist any longer give it to priv domain. */ -- if (!ret) { -- /* -- * In theory we'd need to update the number of dom0 nodes here, -- * but we could be called for a read of the node. So better -- * avoid the risk to overflow the node count of dom0. -- */ -- node->perms.p[0].id = priv_domid; -- } -- - for (i = 1; i < node->perms.num; i++) { - if (node->perms.p[i].perms & XS_PERM_IGNORE) - continue; -@@ -986,15 +1028,15 @@ void domain_entry_dec(struct connection *conn, struct node *node) - struct domain *d; - unsigned int domid; - -- if (!conn) -+ if (!node->perms.p) - return; - - domid = node->perms.p ? node->perms.p[0].id : conn->id; - -- if (conn->transaction) { -+ if (conn && conn->transaction) { - transaction_entry_dec(conn->transaction, domid); - } else { -- d = (domid == conn->id && conn->domain) ? conn->domain -+ d = (conn && domid == conn->id && conn->domain) ? conn->domain - : find_domain_struct(domid); - if (d) { - d->nbentry--; -@@ -1113,7 +1155,7 @@ int domain_memory_add(unsigned int domid, int mem, bool no_quota_check) - * exist, as accounting is done either for a domain related to - * the current connection, or for the domain owning a node - * (which is always existing, as the owner of the node is -- * tested to exist and replaced by domid 0 if not). -+ * tested to exist and deleted or replaced by domid 0 if not). - * So not finding the related domain MUST be an error in the - * data base. - */ -diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h -index 7fe0a21d9e45..b38c82991dc6 100644 ---- a/tools/xenstore/xenstored_domain.h -+++ b/tools/xenstore/xenstored_domain.h -@@ -62,7 +62,7 @@ const char *get_implicit_path(const struct connection *conn); - bool domain_is_unprivileged(struct connection *conn); - - /* Remove node permissions for no longer existing domains. */ --int domain_adjust_node_perms(struct connection *conn, struct node *node); -+int domain_adjust_node_perms(struct node *node); - int domain_alloc_permrefs(struct node_perms *perms); - - /* Quota manipulation */ diff --git a/xsa419-xenstored-02.patch b/xsa419-xenstored-02.patch deleted file mode 100644 index e5d46a2..0000000 --- a/xsa419-xenstored-02.patch +++ /dev/null @@ -1,93 +0,0 @@ -From 929da557efea6c7d2340467d9a7fdae7fda6d2b1 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:13 +0200 -Subject: tools/xenstore: make the internal memory data base the default - -Having a file backed data base has the only advantage of being capable -to dump the contents of it while Xenstore is running, and potentially -using less swap space in case the data base can't be kept in memory. - -It has the major disadvantage of a huge performance overhead: switching -to keep the data base in memory only speeds up live update of xenstored -with 120000 nodes from 20 minutes to 11 seconds. A complete tree walk -of this configuration will be reduced from 7 seconds to 280 msecs -(measured by "xenstore-control check"). - -So make the internal memory data base the default and enhance the -"--internal-db" command line parameter to take an optional parameter -allowing to switch the internal data base back to the file based one. - -This is part of XSA-419. - -Reported-by: Juergen Gross -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall - -diff --git a/tools/helpers/init-xenstore-domain.c b/tools/helpers/init-xenstore-domain.c -index 2d9ab6f1c583..04e351ca29a8 100644 ---- a/tools/helpers/init-xenstore-domain.c -+++ b/tools/helpers/init-xenstore-domain.c -@@ -222,9 +222,9 @@ static int build(xc_interface *xch) - } - - if ( param ) -- snprintf(cmdline, 512, "--event %d --internal-db %s", rv, param); -+ snprintf(cmdline, 512, "--event %d %s", rv, param); - else -- snprintf(cmdline, 512, "--event %d --internal-db", rv); -+ snprintf(cmdline, 512, "--event %d", rv); - - dom->guest_domid = domid; - dom->cmdline = xc_dom_strdup(dom, cmdline); -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 13e48aaa731c..36fb4a832834 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -2308,7 +2308,7 @@ static void accept_connection(int sock) - } - #endif - --static int tdb_flags; -+static int tdb_flags = TDB_INTERNAL | TDB_NOLOCK; - - /* We create initial nodes manually. */ - static void manual_node(const char *name, const char *child) -@@ -2618,7 +2618,8 @@ static void usage(void) - " watch-event: time a watch-event is kept pending\n" - " -R, --no-recovery to request that no recovery should be attempted when\n" - " the store is corrupted (debug only),\n" --" -I, --internal-db store database in memory, not on disk\n" -+" -I, --internal-db [on|off] store database in memory, not on disk, default is\n" -+" memory, with \"--internal-db off\" it is on disk\n" - " -K, --keep-orphans don't delete nodes owned by a domain when the\n" - " domain is deleted (this is a security risk!)\n" - " -V, --verbose to request verbose execution.\n"); -@@ -2644,7 +2645,7 @@ static struct option options[] = { - { "quota-soft", 1, NULL, 'q' }, - { "timeout", 1, NULL, 'w' }, - { "no-recovery", 0, NULL, 'R' }, -- { "internal-db", 0, NULL, 'I' }, -+ { "internal-db", 2, NULL, 'I' }, - { "keep-orphans", 0, NULL, 'K' }, - { "verbose", 0, NULL, 'V' }, - { "watch-nb", 1, NULL, 'W' }, -@@ -2725,7 +2726,8 @@ int main(int argc, char *argv[]) - orig_argc = argc; - orig_argv = argv; - -- while ((opt = getopt_long(argc, argv, "DE:F:HKNPS:t:A:M:Q:q:T:RVW:w:U", -+ while ((opt = getopt_long(argc, argv, -+ "DE:F:HI::KNPS:t:A:M:Q:q:T:RVW:w:U", - options, NULL)) != -1) { - switch (opt) { - case 'D': -@@ -2759,7 +2761,8 @@ int main(int argc, char *argv[]) - tracefile = optarg; - break; - case 'I': -- tdb_flags = TDB_INTERNAL|TDB_NOLOCK; -+ if (optarg && !strcmp(optarg, "off")) -+ tdb_flags = 0; - break; - case 'K': - keep_orphans = true; diff --git a/xsa419-xenstored-03.patch b/xsa419-xenstored-03.patch deleted file mode 100644 index 38130c0..0000000 --- a/xsa419-xenstored-03.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 54e63b7e7c42e4f975163809a01574e78552a6ab Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:13 +0200 -Subject: docs: enhance xenstore.txt with permissions description -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The permission scheme of Xenstore nodes is not really covered by -docs/misc/xenstore.txt, other than referring to the Xen wiki. - -Add a paragraph explaining the permissions of nodes, and especially -mentioning removal of nodes when a domain has been removed from -Xenstore. - -This is part of XSA-419. - -Reported-by: Juergen Gross -Signed-off-by: Juergen Gross -Reviewed-by: Edwin Török -Acked-by: Julien Grall - -diff --git a/docs/misc/xenstore.txt b/docs/misc/xenstore.txt -index 988ef89cba2d..44428ae3a755 100644 ---- a/docs/misc/xenstore.txt -+++ b/docs/misc/xenstore.txt -@@ -43,6 +43,17 @@ bytes are forbidden; clients specifying relative paths should keep - them to within 2048 bytes. (See XENSTORE_*_PATH_MAX in xs_wire.h.) - - -+Each node has one or multiple permission entries. Permissions are -+granted by domain-id, the first permission entry of each node specifies -+the owner of the node. Permissions of a node can be changed by the -+owner of the node, the owner can only be modified by the control -+domain (usually domain id 0). The owner always has the right to read -+and write the node, while other permissions can be setup to allow -+read and/or write access. When a domain is being removed from Xenstore -+nodes owned by that domain will be removed together with all of those -+nodes' children. -+ -+ - Communication with xenstore is via either sockets, or event channel - and shared memory, as specified in io/xs_wire.h: each message in - either direction is a header formatted as a struct xsd_sockmsg diff --git a/xsa420.patch b/xsa420.patch deleted file mode 100644 index 5d00dc2..0000000 --- a/xsa420.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 210879456769ca211c6630f47399ca7a61a37f35 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Date: Wed, 12 Oct 2022 19:13:05 +0100 -Subject: tools/ocaml: Ensure packet size is never negative -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Integers in Ocaml have 63 or 31 bits of signed precision. - -On 64-bit builds of Ocaml, this is fine because a C uint32_t always fits -within a 63-bit signed integer. - -In 32-bit builds of Ocaml, this goes wrong. The C uint32_t is truncated -first (loses the top bit), then has a unsigned/signed mismatch. - -A "negative" value (i.e. a packet on the ring of between 1G and 2G in size) -will trigger an exception later in Bytes.make in xb.ml, and because the packet -is not removed from the ring, the exception re-triggers on every subsequent -query, creating a livelock. - -Fix both the source of the exception in Xb, and as defence in depth, mark the -domain as bad for any Invalid_argument exceptions to avoid the risk of -livelock. - -This is XSA-420 / CVE-2022-42324. - -Reported-by: Juergen Gross -Signed-off-by: Edwin Török -Acked-by: Christian Lindig - -diff --git a/tools/ocaml/libs/xb/partial.ml b/tools/ocaml/libs/xb/partial.ml -index b6e2a716e263..3aa8927eb7f0 100644 ---- a/tools/ocaml/libs/xb/partial.ml -+++ b/tools/ocaml/libs/xb/partial.ml -@@ -36,7 +36,7 @@ let of_string s = - This will leave the guest connection is a bad state and will - be hard to recover from without restarting the connection - (ie rebooting the guest) *) -- let dlen = min xenstore_payload_max dlen in -+ let dlen = max 0 (min xenstore_payload_max dlen) in - { - tid = tid; - rid = rid; -@@ -46,8 +46,8 @@ let of_string s = - } - - let append pkt s sz = -- if pkt.len > 4096 then failwith "Buffer.add: cannot grow buffer"; -- Buffer.add_string pkt.buf (String.sub s 0 sz) -+ if Buffer.length pkt.buf + sz > xenstore_payload_max then failwith "Buffer.add: cannot grow buffer"; -+ Buffer.add_substring pkt.buf s 0 sz - - let to_complete pkt = - pkt.len - (Buffer.length pkt.buf) -diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml -index 5f439fe59f47..f3a71b24ad94 100644 ---- a/tools/ocaml/xenstored/process.ml -+++ b/tools/ocaml/xenstored/process.ml -@@ -722,7 +722,7 @@ let do_input store cons doms con = - History.reconnect con; - info "%s reconnection complete" (Connection.get_domstr con); - None -- | Failure exp -> -+ | Invalid_argument exp | Failure exp -> - error "caught exception %s" exp; - error "got a bad client %s" (sprintf "%-8s" (Connection.get_domstr con)); - Connection.mark_as_bad con; diff --git a/xsa421-01.patch b/xsa421-01.patch deleted file mode 100644 index 10960ba..0000000 --- a/xsa421-01.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 9166869e7e6530befddfd8bb46ff37436a38efc1 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:13 +0200 -Subject: tools/xenstore: fix deleting node in transaction - -In case a node has been created in a transaction and it is later -deleted in the same transaction, the transaction will be terminated -with an error. - -As this error is encountered only when handling the deleted node at -transaction finalization, the transaction will have been performed -partially and without updating the accounting information. This will -enable a malicious guest to create arbitrary number of nodes. - -This is part of XSA-421 / CVE-2022-42325. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Tested-by: Julien Grall -Reviewed-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index 3e3eb47326cc..7ffe21bb5285 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -418,7 +418,13 @@ static int finalize_transaction(struct connection *conn, - true); - talloc_free(data.dptr); - } else { -- ret = do_tdb_delete(conn, &key, NULL); -+ /* -+ * A node having been created and later deleted -+ * in this transaction will have no generation -+ * information stored. -+ */ -+ ret = (i->generation == NO_GENERATION) -+ ? 0 : do_tdb_delete(conn, &key, NULL); - } - if (ret) - goto err; diff --git a/xsa421-02.patch b/xsa421-02.patch deleted file mode 100644 index 1a0b5cd..0000000 --- a/xsa421-02.patch +++ /dev/null @@ -1,401 +0,0 @@ -From 09fc22fea8a4689c5e563ba4a2fa959282071792 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 13 Sep 2022 07:35:14 +0200 -Subject: tools/xenstore: harden transaction finalization against errors - -When finalizing a transaction, any error occurring after checking for -conflicts will result in the transaction being performed only -partially today. Additionally accounting data will not be updated at -the end of the transaction, which might result in further problems -later. - -Avoid those problems by multiple modifications: - -- free any transaction specific nodes which don't need to be committed - as they haven't been written during the transaction as soon as their - generation count has been verified, this will reduce the risk of - out-of-memory situations - -- store the transaction specific node name in struct accessed_node in - order to avoid the need to allocate additional memory for it when - finalizing the transaction - -- don't stop the transaction finalization when hitting an error - condition, but try to continue to handle all modified nodes - -- in case of a detected error do the accounting update as needed and - call the data base checking only after that - -- if writing a node in a transaction is failing (e.g. due to a failed - quota check), fail the transaction, as prior changes to struct - accessed_node can't easily be undone in that case - -This is part of XSA-421 / CVE-2022-42326. - -Reported-by: Julien Grall -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall -Tested-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 36fb4a832834..476d5c6d51bd 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -723,8 +723,7 @@ struct node *read_node(struct connection *conn, const void *ctx, - return NULL; - } - -- if (transaction_prepend(conn, name, &key)) -- return NULL; -+ transaction_prepend(conn, name, &key); - - data = tdb_fetch(tdb_ctx, key); - -@@ -842,10 +841,21 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, - static int write_node(struct connection *conn, struct node *node, - bool no_quota_check) - { -+ int ret; -+ - if (access_node(conn, node, NODE_ACCESS_WRITE, &node->key)) - return errno; - -- return write_node_raw(conn, &node->key, node, no_quota_check); -+ ret = write_node_raw(conn, &node->key, node, no_quota_check); -+ if (ret && conn && conn->transaction) { -+ /* -+ * Reverting access_node() is hard, so just fail the -+ * transaction. -+ */ -+ fail_transaction(conn->transaction); -+ } -+ -+ return ret; - } - - unsigned int perm_for_conn(struct connection *conn, -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index 7ffe21bb5285..ac854197cadb 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -114,7 +114,8 @@ struct accessed_node - struct list_head list; - - /* The name of the node. */ -- char *node; -+ char *trans_name; /* Transaction specific name. */ -+ char *node; /* Main data base name. */ - - /* Generation count (or NO_GENERATION) for conflict checking. */ - uint64_t generation; -@@ -199,25 +200,20 @@ static char *transaction_get_node_name(void *ctx, struct transaction *trans, - * Prepend the transaction to name if node has been modified in the current - * transaction. - */ --int transaction_prepend(struct connection *conn, const char *name, -- TDB_DATA *key) -+void transaction_prepend(struct connection *conn, const char *name, -+ TDB_DATA *key) - { -- char *tdb_name; -+ struct accessed_node *i; - -- if (!conn || !conn->transaction || -- !find_accessed_node(conn->transaction, name)) { -- set_tdb_key(name, key); -- return 0; -+ if (conn && conn->transaction) { -+ i = find_accessed_node(conn->transaction, name); -+ if (i) { -+ set_tdb_key(i->trans_name, key); -+ return; -+ } - } - -- tdb_name = transaction_get_node_name(conn->transaction, -- conn->transaction, name); -- if (!tdb_name) -- return errno; -- -- set_tdb_key(tdb_name, key); -- -- return 0; -+ set_tdb_key(name, key); - } - - /* -@@ -240,7 +236,6 @@ int access_node(struct connection *conn, struct node *node, - struct accessed_node *i = NULL; - struct transaction *trans; - TDB_DATA local_key; -- const char *trans_name = NULL; - int ret; - bool introduce = false; - -@@ -259,10 +254,6 @@ int access_node(struct connection *conn, struct node *node, - - trans = conn->transaction; - -- trans_name = transaction_get_node_name(node, trans, node->name); -- if (!trans_name) -- goto nomem; -- - i = find_accessed_node(trans, node->name); - if (!i) { - if (trans->nodes >= quota_trans_nodes && -@@ -273,9 +264,10 @@ int access_node(struct connection *conn, struct node *node, - i = talloc_zero(trans, struct accessed_node); - if (!i) - goto nomem; -- i->node = talloc_strdup(i, node->name); -- if (!i->node) -+ i->trans_name = transaction_get_node_name(i, trans, node->name); -+ if (!i->trans_name) - goto nomem; -+ i->node = strchr(i->trans_name, '/') + 1; - if (node->generation != NO_GENERATION && node->perms.num) { - i->perms.p = talloc_array(i, struct xs_permissions, - node->perms.num); -@@ -302,7 +294,7 @@ int access_node(struct connection *conn, struct node *node, - i->generation = node->generation; - i->check_gen = true; - if (node->generation != NO_GENERATION) { -- set_tdb_key(trans_name, &local_key); -+ set_tdb_key(i->trans_name, &local_key); - ret = write_node_raw(conn, &local_key, node, true); - if (ret) - goto err; -@@ -321,7 +313,7 @@ int access_node(struct connection *conn, struct node *node, - return -1; - - if (key) { -- set_tdb_key(trans_name, key); -+ set_tdb_key(i->trans_name, key); - if (type == NODE_ACCESS_WRITE) - i->ta_node = true; - if (type == NODE_ACCESS_DELETE) -@@ -333,7 +325,6 @@ int access_node(struct connection *conn, struct node *node, - nomem: - ret = ENOMEM; - err: -- talloc_free((void *)trans_name); - talloc_free(i); - trans->fail = true; - errno = ret; -@@ -371,100 +362,90 @@ void queue_watches(struct connection *conn, const char *name, bool watch_exact) - * base. - */ - static int finalize_transaction(struct connection *conn, -- struct transaction *trans) -+ struct transaction *trans, bool *is_corrupt) - { -- struct accessed_node *i; -+ struct accessed_node *i, *n; - TDB_DATA key, ta_key, data; - struct xs_tdb_record_hdr *hdr; - uint64_t gen; -- char *trans_name; -- int ret; - -- list_for_each_entry(i, &trans->accessed, list) { -- if (!i->check_gen) -- continue; -+ list_for_each_entry_safe(i, n, &trans->accessed, list) { -+ if (i->check_gen) { -+ set_tdb_key(i->node, &key); -+ data = tdb_fetch(tdb_ctx, key); -+ hdr = (void *)data.dptr; -+ if (!data.dptr) { -+ if (tdb_error(tdb_ctx) != TDB_ERR_NOEXIST) -+ return EIO; -+ gen = NO_GENERATION; -+ } else -+ gen = hdr->generation; -+ talloc_free(data.dptr); -+ if (i->generation != gen) -+ return EAGAIN; -+ } - -- set_tdb_key(i->node, &key); -- data = tdb_fetch(tdb_ctx, key); -- hdr = (void *)data.dptr; -- if (!data.dptr) { -- if (tdb_error(tdb_ctx) != TDB_ERR_NOEXIST) -- return EIO; -- gen = NO_GENERATION; -- } else -- gen = hdr->generation; -- talloc_free(data.dptr); -- if (i->generation != gen) -- return EAGAIN; -+ /* Entries for unmodified nodes can be removed early. */ -+ if (!i->modified) { -+ if (i->ta_node) { -+ set_tdb_key(i->trans_name, &ta_key); -+ if (do_tdb_delete(conn, &ta_key, NULL)) -+ return EIO; -+ } -+ list_del(&i->list); -+ talloc_free(i); -+ } - } - - while ((i = list_top(&trans->accessed, struct accessed_node, list))) { -- trans_name = transaction_get_node_name(i, trans, i->node); -- if (!trans_name) -- /* We are doomed: the transaction is only partial. */ -- goto err; -- -- set_tdb_key(trans_name, &ta_key); -- -- if (i->modified) { -- set_tdb_key(i->node, &key); -- if (i->ta_node) { -- data = tdb_fetch(tdb_ctx, ta_key); -- if (!data.dptr) -- goto err; -+ set_tdb_key(i->node, &key); -+ if (i->ta_node) { -+ set_tdb_key(i->trans_name, &ta_key); -+ data = tdb_fetch(tdb_ctx, ta_key); -+ if (data.dptr) { - hdr = (void *)data.dptr; - hdr->generation = ++generation; -- ret = do_tdb_write(conn, &key, &data, NULL, -- true); -+ *is_corrupt |= do_tdb_write(conn, &key, &data, -+ NULL, true); - talloc_free(data.dptr); -+ if (do_tdb_delete(conn, &ta_key, NULL)) -+ *is_corrupt = true; - } else { -- /* -- * A node having been created and later deleted -- * in this transaction will have no generation -- * information stored. -- */ -- ret = (i->generation == NO_GENERATION) -- ? 0 : do_tdb_delete(conn, &key, NULL); -- } -- if (ret) -- goto err; -- if (i->fire_watch) { -- fire_watches(conn, trans, i->node, NULL, -- i->watch_exact, -- i->perms.p ? &i->perms : NULL); -+ *is_corrupt = true; - } -+ } else { -+ /* -+ * A node having been created and later deleted -+ * in this transaction will have no generation -+ * information stored. -+ */ -+ *is_corrupt |= (i->generation == NO_GENERATION) -+ ? false -+ : do_tdb_delete(conn, &key, NULL); - } -+ if (i->fire_watch) -+ fire_watches(conn, trans, i->node, NULL, i->watch_exact, -+ i->perms.p ? &i->perms : NULL); - -- if (i->ta_node && do_tdb_delete(conn, &ta_key, NULL)) -- goto err; - list_del(&i->list); - talloc_free(i); - } - - return 0; -- --err: -- corrupt(conn, "Partial transaction"); -- return EIO; - } - - static int destroy_transaction(void *_transaction) - { - struct transaction *trans = _transaction; - struct accessed_node *i; -- char *trans_name; - TDB_DATA key; - - wrl_ntransactions--; - trace_destroy(trans, "transaction"); - while ((i = list_top(&trans->accessed, struct accessed_node, list))) { - if (i->ta_node) { -- trans_name = transaction_get_node_name(i, trans, -- i->node); -- if (trans_name) { -- set_tdb_key(trans_name, &key); -- do_tdb_delete(trans->conn, &key, NULL); -- } -+ set_tdb_key(i->trans_name, &key); -+ do_tdb_delete(trans->conn, &key, NULL); - } - list_del(&i->list); - talloc_free(i); -@@ -556,6 +537,7 @@ int do_transaction_end(const void *ctx, struct connection *conn, - { - const char *arg = onearg(in); - struct transaction *trans; -+ bool is_corrupt = false; - int ret; - - if (!arg || (!streq(arg, "T") && !streq(arg, "F"))) -@@ -579,13 +561,17 @@ int do_transaction_end(const void *ctx, struct connection *conn, - ret = transaction_fix_domains(trans, false); - if (ret) - return ret; -- if (finalize_transaction(conn, trans)) -- return EAGAIN; -+ ret = finalize_transaction(conn, trans, &is_corrupt); -+ if (ret) -+ return ret; - - wrl_apply_debit_trans_commit(conn); - - /* fix domain entry for each changed domain */ - transaction_fix_domains(trans, true); -+ -+ if (is_corrupt) -+ corrupt(conn, "transaction inconsistency"); - } - send_ack(conn, XS_TRANSACTION_END); - -@@ -660,7 +646,7 @@ int check_transactions(struct hashtable *hash) - struct connection *conn; - struct transaction *trans; - struct accessed_node *i; -- char *tname, *tnode; -+ char *tname; - - list_for_each_entry(conn, &connections, list) { - list_for_each_entry(trans, &conn->transaction_list, list) { -@@ -672,11 +658,8 @@ int check_transactions(struct hashtable *hash) - list_for_each_entry(i, &trans->accessed, list) { - if (!i->ta_node) - continue; -- tnode = transaction_get_node_name(tname, trans, -- i->node); -- if (!tnode || !remember_string(hash, tnode)) -+ if (!remember_string(hash, i->trans_name)) - goto nomem; -- talloc_free(tnode); - } - - talloc_free(tname); -diff --git a/tools/xenstore/xenstored_transaction.h b/tools/xenstore/xenstored_transaction.h -index 39d7f81c5127..3417303f9427 100644 ---- a/tools/xenstore/xenstored_transaction.h -+++ b/tools/xenstore/xenstored_transaction.h -@@ -48,8 +48,8 @@ int __must_check access_node(struct connection *conn, struct node *node, - void queue_watches(struct connection *conn, const char *name, bool watch_exact); - - /* Prepend the transaction to name if appropriate. */ --int transaction_prepend(struct connection *conn, const char *name, -- TDB_DATA *key); -+void transaction_prepend(struct connection *conn, const char *name, -+ TDB_DATA *key); - - /* Mark the transaction as failed. This will prevent it to be committed. */ - void fail_transaction(struct transaction *trans); diff --git a/xsa422-4.16-1.patch b/xsa422-4.16-1.patch deleted file mode 100644 index 1d36873..0000000 --- a/xsa422-4.16-1.patch +++ /dev/null @@ -1,70 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Enumeration for IBPB_RET - -The IBPB_RET bit indicates that the CPU's implementation of MSR_PRED_CMD.IBPB -does flush the RSB/RAS too. - -This is part of XSA-422 / CVE-2022-23824. - -Signed-off-by: Andrew Cooper -Acked-by: Jan Beulich - -diff --git a/tools/libs/light/libxl_cpuid.c b/tools/libs/light/libxl_cpuid.c -index bf6fdee360a9..691d5c6b2a68 100644 ---- a/tools/libs/light/libxl_cpuid.c -+++ b/tools/libs/light/libxl_cpuid.c -@@ -289,6 +289,7 @@ int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str) - {"ssb-no", 0x80000008, NA, CPUID_REG_EBX, 26, 1}, - {"psfd", 0x80000008, NA, CPUID_REG_EBX, 28, 1}, - {"btc-no", 0x80000008, NA, CPUID_REG_EBX, 29, 1}, -+ {"ibpb-ret", 0x80000008, NA, CPUID_REG_EBX, 30, 1}, - - {"nc", 0x80000008, NA, CPUID_REG_ECX, 0, 8}, - {"apicidsize", 0x80000008, NA, CPUID_REG_ECX, 12, 4}, -diff --git a/tools/misc/xen-cpuid.c b/tools/misc/xen-cpuid.c -index fe22f5f5b68b..cd094427dd4c 100644 ---- a/tools/misc/xen-cpuid.c -+++ b/tools/misc/xen-cpuid.c -@@ -159,6 +159,7 @@ static const char *const str_e8b[32] = - [24] = "amd-ssbd", [25] = "virt-ssbd", - [26] = "ssb-no", - [28] = "psfd", [29] = "btc-no", -+ [30] = "ibpb-ret", - }; - - static const char *const str_7d0[32] = -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 0f4bad3d3abb..16a562d3a172 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -419,7 +419,7 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - * Hardware read-only information, stating immunity to certain issues, or - * suggestions of which mitigation to use. - */ -- printk(" Hardware hints:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", -+ printk(" Hardware hints:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", - (caps & ARCH_CAPS_RDCL_NO) ? " RDCL_NO" : "", - (caps & ARCH_CAPS_IBRS_ALL) ? " IBRS_ALL" : "", - (caps & ARCH_CAPS_RSBA) ? " RSBA" : "", -@@ -436,7 +436,8 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - (e8b & cpufeat_mask(X86_FEATURE_STIBP_ALWAYS)) ? " STIBP_ALWAYS" : "", - (e8b & cpufeat_mask(X86_FEATURE_IBRS_FAST)) ? " IBRS_FAST" : "", - (e8b & cpufeat_mask(X86_FEATURE_IBRS_SAME_MODE)) ? " IBRS_SAME_MODE" : "", -- (e8b & cpufeat_mask(X86_FEATURE_BTC_NO)) ? " BTC_NO" : ""); -+ (e8b & cpufeat_mask(X86_FEATURE_BTC_NO)) ? " BTC_NO" : "", -+ (e8b & cpufeat_mask(X86_FEATURE_IBPB_RET)) ? " IBPB_RET" : ""); - - /* Hardware features which need driving to mitigate issues. */ - printk(" Hardware features:%s%s%s%s%s%s%s%s%s%s%s%s\n", -diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h -index e7b8167800a2..e0731221404c 100644 ---- a/xen/include/public/arch-x86/cpufeatureset.h -+++ b/xen/include/public/arch-x86/cpufeatureset.h -@@ -267,6 +267,7 @@ XEN_CPUFEATURE(VIRT_SSBD, 8*32+25) /* MSR_VIRT_SPEC_CTRL.SSBD */ - XEN_CPUFEATURE(SSB_NO, 8*32+26) /*A Hardware not vulnerable to SSB */ - XEN_CPUFEATURE(PSFD, 8*32+28) /*S MSR_SPEC_CTRL.PSFD */ - XEN_CPUFEATURE(BTC_NO, 8*32+29) /*A Hardware not vulnerable to Branch Type Confusion */ -+XEN_CPUFEATURE(IBPB_RET, 8*32+30) /*A IBPB clears RSB/RAS too. */ - - /* Intel-defined CPU features, CPUID level 0x00000007:0.edx, word 9 */ - XEN_CPUFEATURE(AVX512_4VNNIW, 9*32+ 2) /*A AVX512 Neural Network Instructions */ diff --git a/xsa422-4.16-2.patch b/xsa422-4.16-2.patch deleted file mode 100644 index a1a2f8d..0000000 --- a/xsa422-4.16-2.patch +++ /dev/null @@ -1,100 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Mitigate IBPB not flushing the RSB/RAS - -Introduce spec_ctrl_new_guest_context() to encapsulate all logic pertaining to -using MSR_PRED_CMD for a new guest context, even if it only has one user -presently. - -Introduce X86_BUG_IBPB_NO_RET, and use it extend spec_ctrl_new_guest_context() -with a manual fixup for hardware which mis-implements IBPB. - -This is part of XSA-422 / CVE-2022-23824. - -Signed-off-by: Andrew Cooper -Acked-by: Jan Beulich - -diff --git a/xen/arch/x86/asm-macros.c b/xen/arch/x86/asm-macros.c -index 7e536b0d82f5..891d86c7655c 100644 ---- a/xen/arch/x86/asm-macros.c -+++ b/xen/arch/x86/asm-macros.c -@@ -1,2 +1,3 @@ - #include - #include -+#include -diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c -index 3fab2364be8d..3080cde62b5b 100644 ---- a/xen/arch/x86/domain.c -+++ b/xen/arch/x86/domain.c -@@ -2092,7 +2092,7 @@ void context_switch(struct vcpu *prev, struct vcpu *next) - */ - if ( *last_id != next_id ) - { -- wrmsrl(MSR_PRED_CMD, PRED_CMD_IBPB); -+ spec_ctrl_new_guest_context(); - *last_id = next_id; - } - } -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 16a562d3a172..90d86fe5cb47 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -805,6 +805,14 @@ static void __init ibpb_calculations(void) - } - - /* -+ * AMD/Hygon CPUs to date (June 2022) don't flush the the RAS. Future -+ * CPUs are expected to enumerate IBPB_RET when this has been fixed. -+ * Until then, cover the difference with the software sequence. -+ */ -+ if ( boot_cpu_has(X86_FEATURE_IBPB) && !boot_cpu_has(X86_FEATURE_IBPB_RET) ) -+ setup_force_cpu_cap(X86_BUG_IBPB_NO_RET); -+ -+ /* - * IBPB-on-entry mitigations for Branch Type Confusion. - * - * IBPB && !BTC_NO selects all AMD/Hygon hardware, not known to be safe, -diff --git a/xen/include/asm-x86/cpufeatures.h b/xen/include/asm-x86/cpufeatures.h -index 672c9ee22ba2..ecc1bb09505a 100644 ---- a/xen/include/asm-x86/cpufeatures.h -+++ b/xen/include/asm-x86/cpufeatures.h -@@ -49,6 +49,7 @@ XEN_CPUFEATURE(IBPB_ENTRY_HVM, X86_SYNTH(29)) /* MSR_PRED_CMD used by Xen for - #define X86_BUG_FPU_PTRS X86_BUG( 0) /* (F)X{SAVE,RSTOR} doesn't save/restore FOP/FIP/FDP. */ - #define X86_BUG_NULL_SEG X86_BUG( 1) /* NULL-ing a selector preserves the base and limit. */ - #define X86_BUG_CLFLUSH_MFENCE X86_BUG( 2) /* MFENCE needed to serialise CLFLUSH */ -+#define X86_BUG_IBPB_NO_RET X86_BUG( 3) /* IBPB doesn't flush the RSB/RAS */ - - /* Total number of capability words, inc synth and bug words. */ - #define NCAPINTS (FSCAPINTS + X86_NR_SYNTH + X86_NR_BUG) /* N 32-bit words worth of info */ -diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h -index 9403b81dc7af..6a77c3937844 100644 ---- a/xen/include/asm-x86/spec_ctrl.h -+++ b/xen/include/asm-x86/spec_ctrl.h -@@ -65,6 +65,28 @@ - void init_speculation_mitigations(void); - void spec_ctrl_init_domain(struct domain *d); - -+/* -+ * Switch to a new guest prediction context. -+ * -+ * This flushes all indirect branch predictors (BTB, RSB/RAS), so guest code -+ * which has previously run on this CPU can't attack subsequent guest code. -+ * -+ * As this flushes the RSB/RAS, it destroys the predictions of the calling -+ * context. For best performace, arrange for this to be used when we're going -+ * to jump out of the current context, e.g. with reset_stack_and_jump(). -+ * -+ * For hardware which mis-implements IBPB, fix up by flushing the RSB/RAS -+ * manually. -+ */ -+static always_inline void spec_ctrl_new_guest_context(void) -+{ -+ wrmsrl(MSR_PRED_CMD, PRED_CMD_IBPB); -+ -+ /* (ab)use alternative_input() to specify clobbers. */ -+ alternative_input("", "DO_OVERWRITE_RSB", X86_BUG_IBPB_NO_RET, -+ : "rax", "rcx"); -+} -+ - extern int8_t opt_ibpb_ctxt_switch; - extern bool opt_ssbd; - extern int8_t opt_eager_fpu; From 06e1d41034b24e4279b51d9f792c049ce0b13404 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 15 Feb 2023 20:11:24 +0000 Subject: [PATCH 02/12] x86: Cross-Thread Return Address Predictions [XSA-426, CVE-2022-27672] --- xen.spec | 8 +++- xsa426.patch | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 xsa426.patch diff --git a/xen.spec b/xen.spec index f2f311e..926a5d8 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.3 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -111,6 +111,7 @@ Patch43: xen.gcc11.fixes.patch Patch44: xsa376.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch +Patch47: xsa426.patch %if %build_qemutrad @@ -323,6 +324,7 @@ manage Xen virtual machines. %patch44 -p1 %patch45 -p1 %patch46 -p1 +%patch47 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -938,6 +940,10 @@ fi %endif %changelog +* Wed Feb 15 2023 Michael Young - 4.16.3-2 +- x86: Cross-Thread Return Address Predictions [XSA-426, CVE-2022-27672] + (backported to xen-4.16) + * Mon Dec 19 2022 Michael Young - 4.16.3-1 - update to xen-4.16.3 remove or adjust patches now included or superceded upstream diff --git a/xsa426.patch b/xsa426.patch new file mode 100644 index 0000000..6df680c --- /dev/null +++ b/xsa426.patch @@ -0,0 +1,107 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Mitigate Cross-Thread Return Address Predictions + +This is XSA-426 / CVE-2022-27672 + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc +index 923910f553c5..a2ff38cdebf2 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -2355,7 +2355,7 @@ guests to use. + on entry and exit. These blocks are necessary to virtualise support for + guests and if disabled, guests will be unable to use IBRS/STIBP/SSBD/etc. + * `rsb=` offers control over whether to overwrite the Return Stack Buffer / +- Return Address Stack on entry to Xen. ++ Return Address Stack on entry to Xen and on idle. + * `md-clear=` offers control over whether to use VERW to flush + microarchitectural buffers on idle and exit from Xen. *Note: For + compatibility with development versions of this fix, `mds=` is also accepted +diff --git a/xen/arch/x86/include/asm/cpufeatures.h b/xen/arch/x86/include/asm/cpufeatures.h +index 865f1109866d..da0593de8542 100644 +--- a/xen/include/asm-x86/cpufeatures.h ++++ b/xen/include/asm-x86/cpufeatures.h +@@ -35,7 +35,8 @@ XEN_CPUFEATURE(SC_RSB_HVM, X86_SYNTH(19)) /* RSB overwrite needed for HVM + XEN_CPUFEATURE(XEN_SELFSNOOP, X86_SYNTH(20)) /* SELFSNOOP gets used by Xen itself */ + XEN_CPUFEATURE(SC_MSR_IDLE, X86_SYNTH(21)) /* Clear MSR_SPEC_CTRL on idle */ + XEN_CPUFEATURE(XEN_LBR, X86_SYNTH(22)) /* Xen uses MSR_DEBUGCTL.LBR */ +-/* Bits 23,24 unused. */ ++/* Bits 23 unused. */ ++XEN_CPUFEATURE(SC_RSB_IDLE, X86_SYNTH(24)) /* RSB overwrite needed for idle. */ + XEN_CPUFEATURE(SC_VERW_IDLE, X86_SYNTH(25)) /* VERW used by Xen for idle */ + XEN_CPUFEATURE(XEN_SHSTK, X86_SYNTH(26)) /* Xen uses CET Shadow Stacks */ + XEN_CPUFEATURE(XEN_IBT, X86_SYNTH(27)) /* Xen uses CET Indirect Branch Tracking */ +diff --git a/xen/arch/x86/include/asm/spec_ctrl.h b/xen/arch/x86/include/asm/spec_ctrl.h +index 6a77c3937844..391973ef6a28 100644 +--- a/xen/include/asm-x86/spec_ctrl.h ++++ b/xen/include/asm-x86/spec_ctrl.h +@@ -159,6 +159,21 @@ static always_inline void spec_ctrl_enter_idle(struct cpu_info *info) + */ + alternative_input("", "verw %[sel]", X86_FEATURE_SC_VERW_IDLE, + [sel] "m" (info->verw_sel)); ++ ++ /* ++ * Cross-Thread Return Address Predictions: ++ * ++ * On vulnerable systems, the return predictions (RSB/RAS) are statically ++ * partitioned between active threads. When entering idle, our entries ++ * are re-partitioned to allow the other threads to use them. ++ * ++ * In some cases, we might still have guest entries in the RAS, so flush ++ * them before injecting them sideways to our sibling thread. ++ * ++ * (ab)use alternative_input() to specify clobbers. ++ */ ++ alternative_input("", "DO_OVERWRITE_RSB", X86_FEATURE_SC_RSB_IDLE, ++ : "rax", "rcx"); + } + + /* WARNING! `ret`, `call *`, `jmp *` not safe before this call. */ +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index a320b81947c8..e80e2a5ed1a9 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -1327,13 +1327,38 @@ void __init init_speculation_mitigations(void) + * 3) Some CPUs have RSBs which are not full width, which allow the + * attacker's entries to alias Xen addresses. + * ++ * 4) Some CPUs have RSBs which are re-partitioned based on thread ++ * idleness, which allows an attacker to inject entries into the other ++ * thread. We still active the optimisation in this case, and mitigate ++ * in the idle path which has lower overhead. ++ * + * It is safe to turn off RSB stuffing when Xen is using SMEP itself, and + * 32bit PV guests are disabled, and when the RSB is full width. + */ + BUILD_BUG_ON(RO_MPT_VIRT_START != PML4_ADDR(256)); +- if ( opt_rsb_pv == -1 && boot_cpu_has(X86_FEATURE_XEN_SMEP) && +- !opt_pv32 && rsb_is_full_width() ) +- opt_rsb_pv = 0; ++ if ( opt_rsb_pv == -1 ) ++ { ++ opt_rsb_pv = (opt_pv32 || !boot_cpu_has(X86_FEATURE_XEN_SMEP) || ++ !rsb_is_full_width()); ++ ++ /* ++ * Cross-Thread Return Address Predictions. ++ * ++ * Vulnerable systems are Zen1/Zen2 uarch, which is AMD Fam17 / Hygon ++ * Fam18, when SMT is active. ++ * ++ * To mitigate, we must flush the RSB/RAS/RAP once between entering ++ * Xen and going idle. ++ * ++ * Most cases flush on entry to Xen anyway. The one case where we ++ * don't is when using the SMEP optimisation for PV guests. Flushing ++ * before going idle is less overhead than flushing on PV entry. ++ */ ++ if ( !opt_rsb_pv && hw_smt_enabled && ++ (boot_cpu_data.x86_vendor & (X86_VENDOR_AMD|X86_VENDOR_HYGON)) && ++ (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) ) ++ setup_force_cpu_cap(X86_FEATURE_SC_RSB_IDLE); ++ } + + if ( opt_rsb_pv ) + { From 0b14ad1009fe1f04032d24f87174f1e94d5021a5 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Sat, 18 Feb 2023 11:40:29 +0000 Subject: [PATCH 03/12] use OVMF.fd from new edk2-ovmf-xen package ovmf.bin file built from edk2-ovmf package no longer supports xen (#2170930) --- xen.spec | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/xen.spec b/xen.spec index 926a5d8..12671b1 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.3 -Release: 2%{?dist} +Release: 3%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -178,9 +178,6 @@ BuildRequires: systemd-devel %ifarch armv7hl aarch64 BuildRequires: libfdt-devel %endif -%if %build_ovmf -BuildRequires: edk2-ovmf -%endif %if %build_hyp BuildRequires: bison flex %endif @@ -215,6 +212,9 @@ Recommends: qemu-system-x86-core # rom file for qemu-xen-traditional Recommends: ipxe-roms-qemu %endif +%if %build_ovmf +Recommends: edk2-ovmf-xen +%endif %description runtime This package contains the runtime programs and daemons which @@ -376,7 +376,7 @@ CONFIG_EXTRA="--enable-qemu-traditional" CONFIG_EXTRA="" %endif %if %build_ovmf -CONFIG_EXTRA="$CONFIG_EXTRA --with-system-ovmf=%{_libexecdir}/%{name}/boot/ovmf.bin" +CONFIG_EXTRA="$CONFIG_EXTRA --with-system-ovmf=/usr/share/edk2/xen/OVMF.fd" %endif %ifnarch armv7hl aarch64 CONFIG_EXTRA="$CONFIG_EXTRA --with-system-ipxe=/usr/share/ipxe/10ec8139.rom" @@ -510,10 +510,6 @@ strip -s %{buildroot}/%{_libdir}/efi/xen-%{version}.efi rm -rf %{buildroot}/%{_unitdir}/oxenstored.service %endif -%if %build_ovmf -cat /usr/share/OVMF/OVMF_{VARS,CODE}.fd >%{buildroot}%{_libexecdir}/%{name}/boot/ovmf.bin -%endif - ############ fixup files in /etc ############ # logrotate @@ -792,9 +788,6 @@ fi %{_libexecdir}/%{name}/boot/xen-shim /usr/lib/debug%{_libexecdir}/xen/boot/xen-shim-syms %endif -%if %build_ovmf -%{_libexecdir}/xen/boot/ovmf.bin -%endif %if %build_stubdom %if %build_qemutrad %{_libexecdir}/xen/boot/ioemu-stubdom.gz @@ -940,6 +933,10 @@ fi %endif %changelog +* Sat Feb 18 2023 Michael Young - 4.16.3-3 +- use OVMF.fd from new edk2-ovmf-xen package as ovmf.bin file + built from edk2-ovmf package no longer supports xen (#2170930) + * Wed Feb 15 2023 Michael Young - 4.16.3-2 - x86: Cross-Thread Return Address Predictions [XSA-426, CVE-2022-27672] (backported to xen-4.16) From 85b6cafcb8d3d4e0678855a86665516d8762d971 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 21 Mar 2023 23:50:32 +0000 Subject: [PATCH 04/12] 3 security issues (#2180425) x86 shadow plus log-dirty mode use-after-free [XSA-427, CVE-2022-42332] x86/HVM pinned cache attributes mis-handling [XSA-428, CVE-2022-42333, CVE-2022-42334] x86: speculative vulnerability in 32bit SYSCALL path [XSA-429, CVE-2022-42331] --- xen.spec | 18 ++++++- xsa427-4.16.patch | 74 ++++++++++++++++++++++++++++ xsa428-4.16-1.patch | 40 ++++++++++++++++ xsa428-4.16-2.patch | 114 ++++++++++++++++++++++++++++++++++++++++++++ xsa429.patch | 46 ++++++++++++++++++ 5 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 xsa427-4.16.patch create mode 100644 xsa428-4.16-1.patch create mode 100644 xsa428-4.16-2.patch create mode 100644 xsa429.patch diff --git a/xen.spec b/xen.spec index 12671b1..fcd0c28 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.3 -Release: 3%{?dist} +Release: 4%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -112,6 +112,10 @@ Patch44: xsa376.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch Patch47: xsa426.patch +Patch48: xsa427-4.16.patch +Patch49: xsa428-4.16-1.patch +Patch50: xsa428-4.16-2.patch +Patch51: xsa429.patch %if %build_qemutrad @@ -325,6 +329,10 @@ manage Xen virtual machines. %patch45 -p1 %patch46 -p1 %patch47 -p1 +%patch48 -p1 +%patch49 -p1 +%patch50 -p1 +%patch51 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -933,6 +941,14 @@ fi %endif %changelog +* Tue Mar 21 2023 Michael Young - 4.16.3-4 +- 3 security issues (#2180425) + x86 shadow plus log-dirty mode use-after-free [XSA-427, CVE-2022-42332] + x86/HVM pinned cache attributes mis-handling [XSA-428, CVE-2022-42333, + CVE-2022-42334] + x86: speculative vulnerability in 32bit SYSCALL path [XSA-429, + CVE-2022-42331] + * Sat Feb 18 2023 Michael Young - 4.16.3-3 - use OVMF.fd from new edk2-ovmf-xen package as ovmf.bin file built from edk2-ovmf package no longer supports xen (#2170930) diff --git a/xsa427-4.16.patch b/xsa427-4.16.patch new file mode 100644 index 0000000..4b11fd0 --- /dev/null +++ b/xsa427-4.16.patch @@ -0,0 +1,74 @@ +From: Jan Beulich +Subject: x86/shadow: account for log-dirty mode when pre-allocating + +Pre-allocation is intended to ensure that in the course of constructing +or updating shadows there won't be any risk of just made shadows or +shadows being acted upon can disappear under our feet. The amount of +pages pre-allocated then, however, needs to account for all possible +subsequent allocations. While the use in sh_page_fault() accounts for +all shadows which may need making, so far it didn't account for +allocations coming from log-dirty tracking (which piggybacks onto the +P2M allocation functions). + +Since shadow_prealloc() takes a count of shadows (or other data +structures) rather than a count of pages, putting the adjustment at the +call site of this function won't work very well: We simply can't express +the correct count that way in all cases. Instead take care of this in +the function itself, by "snooping" for L1 type requests. (While not +applicable right now, future new request sites of L1 tables would then +also be covered right away.) + +It is relevant to note here that pre-allocations like the one done from +shadow_alloc_p2m_page() are benign when they fall in the "scope" of an +earlier pre-alloc which already included that count: The inner call will +simply find enough pages available then; it'll bail right away. + +This is CVE-2022-42332 / XSA-427. + +Signed-off-by: Jan Beulich +Reviewed-by: Tim Deegan + +--- a/xen/include/asm-x86/paging.h ++++ b/xen/include/asm-x86/paging.h +@@ -192,6 +192,10 @@ int paging_mfn_is_dirty(struct domain *d + #define L4_LOGDIRTY_IDX(pfn) ((pfn_x(pfn) >> (PAGE_SHIFT + 3 + PAGETABLE_ORDER * 2)) & \ + (LOGDIRTY_NODE_ENTRIES-1)) + ++#define paging_logdirty_levels() \ ++ (DIV_ROUND_UP(PADDR_BITS - PAGE_SHIFT - (PAGE_SHIFT + 3), \ ++ PAGE_SHIFT - ilog2(sizeof(mfn_t))) + 1) ++ + #ifdef CONFIG_HVM + /* VRAM dirty tracking support */ + struct sh_dirty_vram { +--- a/xen/arch/x86/mm/paging.c ++++ b/xen/arch/x86/mm/paging.c +@@ -280,6 +280,7 @@ void paging_mark_pfn_dirty(struct domain + if ( unlikely(!VALID_M2P(pfn_x(pfn))) ) + return; + ++ BUILD_BUG_ON(paging_logdirty_levels() != 4); + i1 = L1_LOGDIRTY_IDX(pfn); + i2 = L2_LOGDIRTY_IDX(pfn); + i3 = L3_LOGDIRTY_IDX(pfn); +--- a/xen/arch/x86/mm/shadow/common.c ++++ b/xen/arch/x86/mm/shadow/common.c +@@ -1015,7 +1015,17 @@ bool shadow_prealloc(struct domain *d, u + if ( unlikely(d->is_dying) ) + return false; + +- ret = _shadow_prealloc(d, shadow_size(type) * count); ++ count *= shadow_size(type); ++ /* ++ * Log-dirty handling may result in allocations when populating its ++ * tracking structures. Tie this to the caller requesting space for L1 ++ * shadows. ++ */ ++ if ( paging_mode_log_dirty(d) && ++ ((SHF_L1_ANY | SHF_FL1_ANY) & (1u << type)) ) ++ count += paging_logdirty_levels(); ++ ++ ret = _shadow_prealloc(d, count); + if ( !ret && (!d->is_shutting_down || d->shutdown_code != SHUTDOWN_crash) ) + /* + * Failing to allocate memory required for shadow usage can only result in diff --git a/xsa428-4.16-1.patch b/xsa428-4.16-1.patch new file mode 100644 index 0000000..3852f08 --- /dev/null +++ b/xsa428-4.16-1.patch @@ -0,0 +1,40 @@ +From: Jan Beulich +Subject: x86/HVM: bound number of pinned cache attribute regions + +This is exposed via DMOP, i.e. to potentially not fully privileged +device models. With that we may not permit registration of an (almost) +unbounded amount of such regions. + +This is CVE-2022-42333 / part of XSA-428. + +Reported-by: Andrew Cooper +Fixes: 642123c5123f ("x86/hvm: provide XEN_DMOP_pin_memory_cacheattr") +Signed-off-by: Jan Beulich +Reviewed-by: Andrew Cooper + +--- a/xen/arch/x86/hvm/mtrr.c ++++ b/xen/arch/x86/hvm/mtrr.c +@@ -595,6 +595,7 @@ int hvm_set_mem_pinned_cacheattr(struct + uint64_t gfn_end, uint32_t type) + { + struct hvm_mem_pinned_cacheattr_range *range; ++ unsigned int nr = 0; + int rc = 1; + + if ( !is_hvm_domain(d) ) +@@ -666,11 +667,15 @@ int hvm_set_mem_pinned_cacheattr(struct + rc = -EBUSY; + break; + } ++ ++nr; + } + rcu_read_unlock(&pinned_cacheattr_rcu_lock); + if ( rc <= 0 ) + return rc; + ++ if ( nr >= 64 /* The limit is arbitrary. */ ) ++ return -ENOSPC; ++ + range = xzalloc(struct hvm_mem_pinned_cacheattr_range); + if ( range == NULL ) + return -ENOMEM; diff --git a/xsa428-4.16-2.patch b/xsa428-4.16-2.patch new file mode 100644 index 0000000..4b3f331 --- /dev/null +++ b/xsa428-4.16-2.patch @@ -0,0 +1,114 @@ +From: Jan Beulich +Subject: x86/HVM: serialize pinned cache attribute list manipulation + +While the RCU variants of list insertion and removal allow lockless list +traversal (with RCU just read-locked), insertions and removals still +need serializing amongst themselves. To keep things simple, use the +domain lock for this purpose. + +This is CVE-2022-42334 / part of XSA-428. + +Fixes: 642123c5123f ("x86/hvm: provide XEN_DMOP_pin_memory_cacheattr") +Signed-off-by: Jan Beulich +Reviewed-by: Julien Grall + +--- a/xen/arch/x86/hvm/mtrr.c ++++ b/xen/arch/x86/hvm/mtrr.c +@@ -594,7 +594,7 @@ static void free_pinned_cacheattr_entry( + int hvm_set_mem_pinned_cacheattr(struct domain *d, uint64_t gfn_start, + uint64_t gfn_end, uint32_t type) + { +- struct hvm_mem_pinned_cacheattr_range *range; ++ struct hvm_mem_pinned_cacheattr_range *range, *newr; + unsigned int nr = 0; + int rc = 1; + +@@ -608,14 +608,15 @@ int hvm_set_mem_pinned_cacheattr(struct + { + case XEN_DOMCTL_DELETE_MEM_CACHEATTR: + /* Remove the requested range. */ +- rcu_read_lock(&pinned_cacheattr_rcu_lock); +- list_for_each_entry_rcu ( range, +- &d->arch.hvm.pinned_cacheattr_ranges, +- list ) ++ domain_lock(d); ++ list_for_each_entry ( range, ++ &d->arch.hvm.pinned_cacheattr_ranges, ++ list ) + if ( range->start == gfn_start && range->end == gfn_end ) + { +- rcu_read_unlock(&pinned_cacheattr_rcu_lock); + list_del_rcu(&range->list); ++ domain_unlock(d); ++ + type = range->type; + call_rcu(&range->rcu, free_pinned_cacheattr_entry); + p2m_memory_type_changed(d); +@@ -636,7 +637,7 @@ int hvm_set_mem_pinned_cacheattr(struct + } + return 0; + } +- rcu_read_unlock(&pinned_cacheattr_rcu_lock); ++ domain_unlock(d); + return -ENOENT; + + case PAT_TYPE_UC_MINUS: +@@ -651,7 +652,10 @@ int hvm_set_mem_pinned_cacheattr(struct + return -EINVAL; + } + +- rcu_read_lock(&pinned_cacheattr_rcu_lock); ++ newr = xzalloc(struct hvm_mem_pinned_cacheattr_range); ++ ++ domain_lock(d); ++ + list_for_each_entry_rcu ( range, + &d->arch.hvm.pinned_cacheattr_ranges, + list ) +@@ -669,27 +673,34 @@ int hvm_set_mem_pinned_cacheattr(struct + } + ++nr; + } +- rcu_read_unlock(&pinned_cacheattr_rcu_lock); ++ + if ( rc <= 0 ) +- return rc; ++ /* nothing */; ++ else if ( nr >= 64 /* The limit is arbitrary. */ ) ++ rc = -ENOSPC; ++ else if ( !newr ) ++ rc = -ENOMEM; ++ else ++ { ++ newr->start = gfn_start; ++ newr->end = gfn_end; ++ newr->type = type; + +- if ( nr >= 64 /* The limit is arbitrary. */ ) +- return -ENOSPC; ++ list_add_rcu(&newr->list, &d->arch.hvm.pinned_cacheattr_ranges); ++ ++ newr = NULL; ++ rc = 0; ++ } + +- range = xzalloc(struct hvm_mem_pinned_cacheattr_range); +- if ( range == NULL ) +- return -ENOMEM; ++ domain_unlock(d); + +- range->start = gfn_start; +- range->end = gfn_end; +- range->type = type; ++ xfree(newr); + +- list_add_rcu(&range->list, &d->arch.hvm.pinned_cacheattr_ranges); + p2m_memory_type_changed(d); + if ( type != PAT_TYPE_WRBACK ) + flush_all(FLUSH_CACHE); + +- return 0; ++ return rc; + } + + static int hvm_save_mtrr_msr(struct vcpu *v, hvm_domain_context_t *h) diff --git a/xsa429.patch b/xsa429.patch new file mode 100644 index 0000000..443869f --- /dev/null +++ b/xsa429.patch @@ -0,0 +1,46 @@ +From: Andrew Cooper +Subject: x86/spec-ctrl: Defer CR4_PV32_RESTORE on the cstar_enter path + +As stated (correctly) by the comment next to SPEC_CTRL_ENTRY_FROM_PV, between +the two hunks visible in the patch, RET's are not safe prior to this point. + +CR4_PV32_RESTORE hides a CALL/RET pair in certain configurations (PV32 +compiled in, SMEP or SMAP active), and the RET can be attacked with one of +several known speculative issues. + +Furthermore, CR4_PV32_RESTORE also hides a reference to the cr4_pv32_mask +global variable, which is not safe when XPTI is active before restoring Xen's +full pagetables. + +This crash has gone unnoticed because it is only AMD CPUs which permit the +SYSCALL instruction in compatibility mode, and these are not vulnerable to +Meltdown so don't activate XPTI by default. + +This is XSA-429 / CVE-2022-42331 + +Fixes: 5e7962901131 ("x86/entry: Organise the use of MSR_SPEC_CTRL at each entry/exit point") +Fixes: 5784de3e2067 ("x86: Meltdown band-aid against malicious 64-bit PV guests") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S +index ae012851819a..7675a59ff057 100644 +--- a/xen/arch/x86/x86_64/entry.S ++++ b/xen/arch/x86/x86_64/entry.S +@@ -288,7 +288,6 @@ ENTRY(cstar_enter) + ALTERNATIVE "", "setssbsy", X86_FEATURE_XEN_SHSTK + #endif + push %rax /* Guest %rsp */ +- CR4_PV32_RESTORE + movq 8(%rsp), %rax /* Restore guest %rax. */ + movq $FLAT_USER_SS32, 8(%rsp) /* Assume a 64bit domain. Compat handled lower. */ + pushq %r11 +@@ -312,6 +311,8 @@ ENTRY(cstar_enter) + .Lcstar_cr3_okay: + sti + ++ CR4_PV32_RESTORE ++ + movq STACK_CPUINFO_FIELD(current_vcpu)(%rbx), %rbx + + #ifdef CONFIG_PV32 From 01f64a0113e2a6d60c52fa92778ce0eff6b0e62c Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 2 May 2023 22:54:41 +0100 Subject: [PATCH 05/12] update to xen-4.16.4 remove patches now included upstream --- .gitignore | 2 +- sources | 2 +- xen.spec | 18 +++---- xsa426.patch | 107 ----------------------------------------- xsa427-4.16.patch | 74 ---------------------------- xsa428-4.16-1.patch | 40 ---------------- xsa428-4.16-2.patch | 114 -------------------------------------------- xsa429.patch | 46 ------------------ 8 files changed, 8 insertions(+), 395 deletions(-) delete mode 100644 xsa426.patch delete mode 100644 xsa427-4.16.patch delete mode 100644 xsa428-4.16-1.patch delete mode 100644 xsa428-4.16-2.patch delete mode 100644 xsa429.patch diff --git a/.gitignore b/.gitignore index 3d1112b..d252877 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.16.3.tar.gz +/xen-4.16.4.tar.gz diff --git a/sources b/sources index b3ba74d..55e15c7 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.16.3.tar.gz) = b8cbd6f95681de5f824ada2d3cbe0653a38514a18df0dafcf811fb255219c7abec96f46217bdb0c83e1119f685da9a6af7194eeaa94f1cc3c892702782133b4f +SHA512 (xen-4.16.4.tar.gz) = 1b5a167a9f5537b5311ef1cb56e0e6ffda0e4e569ef0534e47b4f4a528067bda46fb58b2a01abc02e8c38b2b70207fb201a5d4d10274fba70135eaa8e318e055 diff --git a/xen.spec b/xen.spec index fcd0c28..aba2fce 100644 --- a/xen.spec +++ b/xen.spec @@ -54,8 +54,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.16.3 -Release: 4%{?dist} +Version: 4.16.4 +Release: 1%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -111,11 +111,6 @@ Patch43: xen.gcc11.fixes.patch Patch44: xsa376.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch -Patch47: xsa426.patch -Patch48: xsa427-4.16.patch -Patch49: xsa428-4.16-1.patch -Patch50: xsa428-4.16-2.patch -Patch51: xsa429.patch %if %build_qemutrad @@ -328,11 +323,6 @@ manage Xen virtual machines. %patch44 -p1 %patch45 -p1 %patch46 -p1 -%patch47 -p1 -%patch48 -p1 -%patch49 -p1 -%patch50 -p1 -%patch51 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -941,6 +931,10 @@ fi %endif %changelog +* Tue May 02 2023 Michael Young - 4.16.4-1 +- update to xen-4.16.4 + remove patches now included upstream + * Tue Mar 21 2023 Michael Young - 4.16.3-4 - 3 security issues (#2180425) x86 shadow plus log-dirty mode use-after-free [XSA-427, CVE-2022-42332] diff --git a/xsa426.patch b/xsa426.patch deleted file mode 100644 index 6df680c..0000000 --- a/xsa426.patch +++ /dev/null @@ -1,107 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Mitigate Cross-Thread Return Address Predictions - -This is XSA-426 / CVE-2022-27672 - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc -index 923910f553c5..a2ff38cdebf2 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -2355,7 +2355,7 @@ guests to use. - on entry and exit. These blocks are necessary to virtualise support for - guests and if disabled, guests will be unable to use IBRS/STIBP/SSBD/etc. - * `rsb=` offers control over whether to overwrite the Return Stack Buffer / -- Return Address Stack on entry to Xen. -+ Return Address Stack on entry to Xen and on idle. - * `md-clear=` offers control over whether to use VERW to flush - microarchitectural buffers on idle and exit from Xen. *Note: For - compatibility with development versions of this fix, `mds=` is also accepted -diff --git a/xen/arch/x86/include/asm/cpufeatures.h b/xen/arch/x86/include/asm/cpufeatures.h -index 865f1109866d..da0593de8542 100644 ---- a/xen/include/asm-x86/cpufeatures.h -+++ b/xen/include/asm-x86/cpufeatures.h -@@ -35,7 +35,8 @@ XEN_CPUFEATURE(SC_RSB_HVM, X86_SYNTH(19)) /* RSB overwrite needed for HVM - XEN_CPUFEATURE(XEN_SELFSNOOP, X86_SYNTH(20)) /* SELFSNOOP gets used by Xen itself */ - XEN_CPUFEATURE(SC_MSR_IDLE, X86_SYNTH(21)) /* Clear MSR_SPEC_CTRL on idle */ - XEN_CPUFEATURE(XEN_LBR, X86_SYNTH(22)) /* Xen uses MSR_DEBUGCTL.LBR */ --/* Bits 23,24 unused. */ -+/* Bits 23 unused. */ -+XEN_CPUFEATURE(SC_RSB_IDLE, X86_SYNTH(24)) /* RSB overwrite needed for idle. */ - XEN_CPUFEATURE(SC_VERW_IDLE, X86_SYNTH(25)) /* VERW used by Xen for idle */ - XEN_CPUFEATURE(XEN_SHSTK, X86_SYNTH(26)) /* Xen uses CET Shadow Stacks */ - XEN_CPUFEATURE(XEN_IBT, X86_SYNTH(27)) /* Xen uses CET Indirect Branch Tracking */ -diff --git a/xen/arch/x86/include/asm/spec_ctrl.h b/xen/arch/x86/include/asm/spec_ctrl.h -index 6a77c3937844..391973ef6a28 100644 ---- a/xen/include/asm-x86/spec_ctrl.h -+++ b/xen/include/asm-x86/spec_ctrl.h -@@ -159,6 +159,21 @@ static always_inline void spec_ctrl_enter_idle(struct cpu_info *info) - */ - alternative_input("", "verw %[sel]", X86_FEATURE_SC_VERW_IDLE, - [sel] "m" (info->verw_sel)); -+ -+ /* -+ * Cross-Thread Return Address Predictions: -+ * -+ * On vulnerable systems, the return predictions (RSB/RAS) are statically -+ * partitioned between active threads. When entering idle, our entries -+ * are re-partitioned to allow the other threads to use them. -+ * -+ * In some cases, we might still have guest entries in the RAS, so flush -+ * them before injecting them sideways to our sibling thread. -+ * -+ * (ab)use alternative_input() to specify clobbers. -+ */ -+ alternative_input("", "DO_OVERWRITE_RSB", X86_FEATURE_SC_RSB_IDLE, -+ : "rax", "rcx"); - } - - /* WARNING! `ret`, `call *`, `jmp *` not safe before this call. */ -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index a320b81947c8..e80e2a5ed1a9 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -1327,13 +1327,38 @@ void __init init_speculation_mitigations(void) - * 3) Some CPUs have RSBs which are not full width, which allow the - * attacker's entries to alias Xen addresses. - * -+ * 4) Some CPUs have RSBs which are re-partitioned based on thread -+ * idleness, which allows an attacker to inject entries into the other -+ * thread. We still active the optimisation in this case, and mitigate -+ * in the idle path which has lower overhead. -+ * - * It is safe to turn off RSB stuffing when Xen is using SMEP itself, and - * 32bit PV guests are disabled, and when the RSB is full width. - */ - BUILD_BUG_ON(RO_MPT_VIRT_START != PML4_ADDR(256)); -- if ( opt_rsb_pv == -1 && boot_cpu_has(X86_FEATURE_XEN_SMEP) && -- !opt_pv32 && rsb_is_full_width() ) -- opt_rsb_pv = 0; -+ if ( opt_rsb_pv == -1 ) -+ { -+ opt_rsb_pv = (opt_pv32 || !boot_cpu_has(X86_FEATURE_XEN_SMEP) || -+ !rsb_is_full_width()); -+ -+ /* -+ * Cross-Thread Return Address Predictions. -+ * -+ * Vulnerable systems are Zen1/Zen2 uarch, which is AMD Fam17 / Hygon -+ * Fam18, when SMT is active. -+ * -+ * To mitigate, we must flush the RSB/RAS/RAP once between entering -+ * Xen and going idle. -+ * -+ * Most cases flush on entry to Xen anyway. The one case where we -+ * don't is when using the SMEP optimisation for PV guests. Flushing -+ * before going idle is less overhead than flushing on PV entry. -+ */ -+ if ( !opt_rsb_pv && hw_smt_enabled && -+ (boot_cpu_data.x86_vendor & (X86_VENDOR_AMD|X86_VENDOR_HYGON)) && -+ (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) ) -+ setup_force_cpu_cap(X86_FEATURE_SC_RSB_IDLE); -+ } - - if ( opt_rsb_pv ) - { diff --git a/xsa427-4.16.patch b/xsa427-4.16.patch deleted file mode 100644 index 4b11fd0..0000000 --- a/xsa427-4.16.patch +++ /dev/null @@ -1,74 +0,0 @@ -From: Jan Beulich -Subject: x86/shadow: account for log-dirty mode when pre-allocating - -Pre-allocation is intended to ensure that in the course of constructing -or updating shadows there won't be any risk of just made shadows or -shadows being acted upon can disappear under our feet. The amount of -pages pre-allocated then, however, needs to account for all possible -subsequent allocations. While the use in sh_page_fault() accounts for -all shadows which may need making, so far it didn't account for -allocations coming from log-dirty tracking (which piggybacks onto the -P2M allocation functions). - -Since shadow_prealloc() takes a count of shadows (or other data -structures) rather than a count of pages, putting the adjustment at the -call site of this function won't work very well: We simply can't express -the correct count that way in all cases. Instead take care of this in -the function itself, by "snooping" for L1 type requests. (While not -applicable right now, future new request sites of L1 tables would then -also be covered right away.) - -It is relevant to note here that pre-allocations like the one done from -shadow_alloc_p2m_page() are benign when they fall in the "scope" of an -earlier pre-alloc which already included that count: The inner call will -simply find enough pages available then; it'll bail right away. - -This is CVE-2022-42332 / XSA-427. - -Signed-off-by: Jan Beulich -Reviewed-by: Tim Deegan - ---- a/xen/include/asm-x86/paging.h -+++ b/xen/include/asm-x86/paging.h -@@ -192,6 +192,10 @@ int paging_mfn_is_dirty(struct domain *d - #define L4_LOGDIRTY_IDX(pfn) ((pfn_x(pfn) >> (PAGE_SHIFT + 3 + PAGETABLE_ORDER * 2)) & \ - (LOGDIRTY_NODE_ENTRIES-1)) - -+#define paging_logdirty_levels() \ -+ (DIV_ROUND_UP(PADDR_BITS - PAGE_SHIFT - (PAGE_SHIFT + 3), \ -+ PAGE_SHIFT - ilog2(sizeof(mfn_t))) + 1) -+ - #ifdef CONFIG_HVM - /* VRAM dirty tracking support */ - struct sh_dirty_vram { ---- a/xen/arch/x86/mm/paging.c -+++ b/xen/arch/x86/mm/paging.c -@@ -280,6 +280,7 @@ void paging_mark_pfn_dirty(struct domain - if ( unlikely(!VALID_M2P(pfn_x(pfn))) ) - return; - -+ BUILD_BUG_ON(paging_logdirty_levels() != 4); - i1 = L1_LOGDIRTY_IDX(pfn); - i2 = L2_LOGDIRTY_IDX(pfn); - i3 = L3_LOGDIRTY_IDX(pfn); ---- a/xen/arch/x86/mm/shadow/common.c -+++ b/xen/arch/x86/mm/shadow/common.c -@@ -1015,7 +1015,17 @@ bool shadow_prealloc(struct domain *d, u - if ( unlikely(d->is_dying) ) - return false; - -- ret = _shadow_prealloc(d, shadow_size(type) * count); -+ count *= shadow_size(type); -+ /* -+ * Log-dirty handling may result in allocations when populating its -+ * tracking structures. Tie this to the caller requesting space for L1 -+ * shadows. -+ */ -+ if ( paging_mode_log_dirty(d) && -+ ((SHF_L1_ANY | SHF_FL1_ANY) & (1u << type)) ) -+ count += paging_logdirty_levels(); -+ -+ ret = _shadow_prealloc(d, count); - if ( !ret && (!d->is_shutting_down || d->shutdown_code != SHUTDOWN_crash) ) - /* - * Failing to allocate memory required for shadow usage can only result in diff --git a/xsa428-4.16-1.patch b/xsa428-4.16-1.patch deleted file mode 100644 index 3852f08..0000000 --- a/xsa428-4.16-1.patch +++ /dev/null @@ -1,40 +0,0 @@ -From: Jan Beulich -Subject: x86/HVM: bound number of pinned cache attribute regions - -This is exposed via DMOP, i.e. to potentially not fully privileged -device models. With that we may not permit registration of an (almost) -unbounded amount of such regions. - -This is CVE-2022-42333 / part of XSA-428. - -Reported-by: Andrew Cooper -Fixes: 642123c5123f ("x86/hvm: provide XEN_DMOP_pin_memory_cacheattr") -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper - ---- a/xen/arch/x86/hvm/mtrr.c -+++ b/xen/arch/x86/hvm/mtrr.c -@@ -595,6 +595,7 @@ int hvm_set_mem_pinned_cacheattr(struct - uint64_t gfn_end, uint32_t type) - { - struct hvm_mem_pinned_cacheattr_range *range; -+ unsigned int nr = 0; - int rc = 1; - - if ( !is_hvm_domain(d) ) -@@ -666,11 +667,15 @@ int hvm_set_mem_pinned_cacheattr(struct - rc = -EBUSY; - break; - } -+ ++nr; - } - rcu_read_unlock(&pinned_cacheattr_rcu_lock); - if ( rc <= 0 ) - return rc; - -+ if ( nr >= 64 /* The limit is arbitrary. */ ) -+ return -ENOSPC; -+ - range = xzalloc(struct hvm_mem_pinned_cacheattr_range); - if ( range == NULL ) - return -ENOMEM; diff --git a/xsa428-4.16-2.patch b/xsa428-4.16-2.patch deleted file mode 100644 index 4b3f331..0000000 --- a/xsa428-4.16-2.patch +++ /dev/null @@ -1,114 +0,0 @@ -From: Jan Beulich -Subject: x86/HVM: serialize pinned cache attribute list manipulation - -While the RCU variants of list insertion and removal allow lockless list -traversal (with RCU just read-locked), insertions and removals still -need serializing amongst themselves. To keep things simple, use the -domain lock for this purpose. - -This is CVE-2022-42334 / part of XSA-428. - -Fixes: 642123c5123f ("x86/hvm: provide XEN_DMOP_pin_memory_cacheattr") -Signed-off-by: Jan Beulich -Reviewed-by: Julien Grall - ---- a/xen/arch/x86/hvm/mtrr.c -+++ b/xen/arch/x86/hvm/mtrr.c -@@ -594,7 +594,7 @@ static void free_pinned_cacheattr_entry( - int hvm_set_mem_pinned_cacheattr(struct domain *d, uint64_t gfn_start, - uint64_t gfn_end, uint32_t type) - { -- struct hvm_mem_pinned_cacheattr_range *range; -+ struct hvm_mem_pinned_cacheattr_range *range, *newr; - unsigned int nr = 0; - int rc = 1; - -@@ -608,14 +608,15 @@ int hvm_set_mem_pinned_cacheattr(struct - { - case XEN_DOMCTL_DELETE_MEM_CACHEATTR: - /* Remove the requested range. */ -- rcu_read_lock(&pinned_cacheattr_rcu_lock); -- list_for_each_entry_rcu ( range, -- &d->arch.hvm.pinned_cacheattr_ranges, -- list ) -+ domain_lock(d); -+ list_for_each_entry ( range, -+ &d->arch.hvm.pinned_cacheattr_ranges, -+ list ) - if ( range->start == gfn_start && range->end == gfn_end ) - { -- rcu_read_unlock(&pinned_cacheattr_rcu_lock); - list_del_rcu(&range->list); -+ domain_unlock(d); -+ - type = range->type; - call_rcu(&range->rcu, free_pinned_cacheattr_entry); - p2m_memory_type_changed(d); -@@ -636,7 +637,7 @@ int hvm_set_mem_pinned_cacheattr(struct - } - return 0; - } -- rcu_read_unlock(&pinned_cacheattr_rcu_lock); -+ domain_unlock(d); - return -ENOENT; - - case PAT_TYPE_UC_MINUS: -@@ -651,7 +652,10 @@ int hvm_set_mem_pinned_cacheattr(struct - return -EINVAL; - } - -- rcu_read_lock(&pinned_cacheattr_rcu_lock); -+ newr = xzalloc(struct hvm_mem_pinned_cacheattr_range); -+ -+ domain_lock(d); -+ - list_for_each_entry_rcu ( range, - &d->arch.hvm.pinned_cacheattr_ranges, - list ) -@@ -669,27 +673,34 @@ int hvm_set_mem_pinned_cacheattr(struct - } - ++nr; - } -- rcu_read_unlock(&pinned_cacheattr_rcu_lock); -+ - if ( rc <= 0 ) -- return rc; -+ /* nothing */; -+ else if ( nr >= 64 /* The limit is arbitrary. */ ) -+ rc = -ENOSPC; -+ else if ( !newr ) -+ rc = -ENOMEM; -+ else -+ { -+ newr->start = gfn_start; -+ newr->end = gfn_end; -+ newr->type = type; - -- if ( nr >= 64 /* The limit is arbitrary. */ ) -- return -ENOSPC; -+ list_add_rcu(&newr->list, &d->arch.hvm.pinned_cacheattr_ranges); -+ -+ newr = NULL; -+ rc = 0; -+ } - -- range = xzalloc(struct hvm_mem_pinned_cacheattr_range); -- if ( range == NULL ) -- return -ENOMEM; -+ domain_unlock(d); - -- range->start = gfn_start; -- range->end = gfn_end; -- range->type = type; -+ xfree(newr); - -- list_add_rcu(&range->list, &d->arch.hvm.pinned_cacheattr_ranges); - p2m_memory_type_changed(d); - if ( type != PAT_TYPE_WRBACK ) - flush_all(FLUSH_CACHE); - -- return 0; -+ return rc; - } - - static int hvm_save_mtrr_msr(struct vcpu *v, hvm_domain_context_t *h) diff --git a/xsa429.patch b/xsa429.patch deleted file mode 100644 index 443869f..0000000 --- a/xsa429.patch +++ /dev/null @@ -1,46 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Defer CR4_PV32_RESTORE on the cstar_enter path - -As stated (correctly) by the comment next to SPEC_CTRL_ENTRY_FROM_PV, between -the two hunks visible in the patch, RET's are not safe prior to this point. - -CR4_PV32_RESTORE hides a CALL/RET pair in certain configurations (PV32 -compiled in, SMEP or SMAP active), and the RET can be attacked with one of -several known speculative issues. - -Furthermore, CR4_PV32_RESTORE also hides a reference to the cr4_pv32_mask -global variable, which is not safe when XPTI is active before restoring Xen's -full pagetables. - -This crash has gone unnoticed because it is only AMD CPUs which permit the -SYSCALL instruction in compatibility mode, and these are not vulnerable to -Meltdown so don't activate XPTI by default. - -This is XSA-429 / CVE-2022-42331 - -Fixes: 5e7962901131 ("x86/entry: Organise the use of MSR_SPEC_CTRL at each entry/exit point") -Fixes: 5784de3e2067 ("x86: Meltdown band-aid against malicious 64-bit PV guests") -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S -index ae012851819a..7675a59ff057 100644 ---- a/xen/arch/x86/x86_64/entry.S -+++ b/xen/arch/x86/x86_64/entry.S -@@ -288,7 +288,6 @@ ENTRY(cstar_enter) - ALTERNATIVE "", "setssbsy", X86_FEATURE_XEN_SHSTK - #endif - push %rax /* Guest %rsp */ -- CR4_PV32_RESTORE - movq 8(%rsp), %rax /* Restore guest %rax. */ - movq $FLAT_USER_SS32, 8(%rsp) /* Assume a 64bit domain. Compat handled lower. */ - pushq %r11 -@@ -312,6 +311,8 @@ ENTRY(cstar_enter) - .Lcstar_cr3_okay: - sti - -+ CR4_PV32_RESTORE -+ - movq STACK_CPUINFO_FIELD(current_vcpu)(%rbx), %rbx - - #ifdef CONFIG_PV32 From 4a3496d719095a51b074423e22e43c0b99d259ad Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 26 Jul 2023 19:57:13 +0100 Subject: [PATCH 06/12] x86/AMD: Zenbleed [XSA-433, CVE-2023-20593] --- xen.spec | 7 ++- xsa433-4.16.patch | 138 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 xsa433-4.16.patch diff --git a/xen.spec b/xen.spec index aba2fce..28c409c 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.4 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -111,6 +111,7 @@ Patch43: xen.gcc11.fixes.patch Patch44: xsa376.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch +Patch47: xsa433-4.16.patch %if %build_qemutrad @@ -323,6 +324,7 @@ manage Xen virtual machines. %patch44 -p1 %patch45 -p1 %patch46 -p1 +%patch47 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -931,6 +933,9 @@ fi %endif %changelog +* Wed Jul 26 2023 Michael Young - 4.16.4-2 +- x86/AMD: Zenbleed [XSA-433, CVE-2023-20593] + * Tue May 02 2023 Michael Young - 4.16.4-1 - update to xen-4.16.4 remove patches now included upstream diff --git a/xsa433-4.16.patch b/xsa433-4.16.patch new file mode 100644 index 0000000..eab4204 --- /dev/null +++ b/xsa433-4.16.patch @@ -0,0 +1,138 @@ +From: Andrew Cooper +Subject: x86/amd: Mitigations for Zenbleed + +Zenbleed is a malfunction on AMD Zen2 uarch parts which results in corruption +of the vector registers. An attacker can trigger this bug deliberately in +order to access stale data in the physical vector register file. This can +include data from sibling threads, or a higher-privilege context. + +Microcode is the preferred mitigation but in the case that's not available use +the chickenbit as instructed by AMD. Re-evaluate the mitigation on late +microcode load too. + +This is XSA-433 / CVE-2023-20593. + +Signed-off-by: Andrew Cooper +Acked-by: Roger Pau Monné + +diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c +index 08e3e1e8a2d8..4ed08df4a8ce 100644 +--- a/xen/arch/x86/cpu/amd.c ++++ b/xen/arch/x86/cpu/amd.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + + #include "cpu.h" + +@@ -769,6 +770,72 @@ void __init detect_zen2_null_seg_behaviour(void) + + } + ++void amd_check_zenbleed(void) ++{ ++ const struct cpu_signature *sig = &this_cpu(cpu_sig); ++ unsigned int good_rev, chickenbit = (1 << 9); ++ uint64_t val, old_val; ++ ++ /* ++ * If we're virtualised, we can't do family/model checks safely, and ++ * we likely wouldn't have access to DE_CFG even if we could see a ++ * microcode revision. ++ * ++ * A hypervisor may hide AVX as a stopgap mitigation. We're not in a ++ * position to care either way. An admin doesn't want to be disabling ++ * AVX as a mitigation on any build of Xen with this logic present. ++ */ ++ if (cpu_has_hypervisor || boot_cpu_data.x86 != 0x17) ++ return; ++ ++ switch (boot_cpu_data.x86_model) { ++ case 0x30 ... 0x3f: good_rev = 0x0830107a; break; ++ case 0x60 ... 0x67: good_rev = 0x0860010b; break; ++ case 0x68 ... 0x6f: good_rev = 0x08608105; break; ++ case 0x70 ... 0x7f: good_rev = 0x08701032; break; ++ case 0xa0 ... 0xaf: good_rev = 0x08a00008; break; ++ default: ++ /* ++ * With the Fam17h check above, parts getting here are Zen1. ++ * They're not affected. ++ */ ++ return; ++ } ++ ++ rdmsrl(MSR_AMD64_DE_CFG, val); ++ old_val = val; ++ ++ /* ++ * Microcode is the preferred mitigation, in terms of performance. ++ * However, without microcode, this chickenbit (specific to the Zen2 ++ * uarch) disables Floating Point Mov-Elimination to mitigate the ++ * issue. ++ */ ++ val &= ~chickenbit; ++ if (sig->rev < good_rev) ++ val |= chickenbit; ++ ++ if (val == old_val) ++ /* Nothing to change. */ ++ return; ++ ++ /* ++ * DE_CFG is a Core-scoped MSR, and this write is racy during late ++ * microcode load. However, both threads calculate the new value from ++ * state which is shared, and unrelated to the old value, so the ++ * result should be consistent. ++ */ ++ wrmsrl(MSR_AMD64_DE_CFG, val); ++ ++ /* ++ * Inform the admin that we changed something, but don't spam, ++ * especially during a late microcode load. ++ */ ++ if (smp_processor_id() == 0) ++ printk(XENLOG_INFO "Zenbleed mitigation - using %s\n", ++ val & chickenbit ? "chickenbit" : "microcode"); ++} ++ + static void init_amd(struct cpuinfo_x86 *c) + { + u32 l, h; +@@ -1041,6 +1108,8 @@ static void init_amd(struct cpuinfo_x86 *c) + if ((smp_processor_id() == 1) && !cpu_has(c, X86_FEATURE_ITSC)) + disable_c1_ramping(); + ++ amd_check_zenbleed(); ++ + check_syscfg_dram_mod_en(); + + amd_log_freq(c); +diff --git a/xen/arch/x86/cpu/microcode/amd.c b/xen/arch/x86/cpu/microcode/amd.c +index 52182c1a2383..483a9b547f80 100644 +--- a/xen/arch/x86/cpu/microcode/amd.c ++++ b/xen/arch/x86/cpu/microcode/amd.c +@@ -262,6 +262,8 @@ static int apply_microcode(const struct microcode_patch *patch) + "microcode: CPU%u updated from revision %#x to %#x, date = %04x-%02x-%02x\n", + cpu, old_rev, rev, patch->year, patch->month, patch->day); + ++ amd_check_zenbleed(); ++ + return 0; + } + +diff --git a/xen/include/asm-x86/processor.h b/xen/include/asm-x86/processor.h +index 3d8aacd3aab2..96621ec39f8b 100644 +--- a/xen/include/asm-x86/processor.h ++++ b/xen/include/asm-x86/processor.h +@@ -639,6 +639,8 @@ enum ap_boot_method { + }; + extern enum ap_boot_method ap_boot_method; + ++void amd_check_zenbleed(void); ++ + #endif /* !__ASSEMBLY__ */ + + #endif /* __ASM_X86_PROCESSOR_H */ + From 8f43f4c45b933595a24b180896034fce0b91e8bf Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 31 Jul 2023 22:08:57 +0100 Subject: [PATCH 07/12] bugfix for x86/AMD: Zenbleed [XSA-433, CVE-2023-20593] --- xen.spec | 7 ++++++- xsa433-bugfix.patch | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 xsa433-bugfix.patch diff --git a/xen.spec b/xen.spec index 28c409c..96a43c7 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.4 -Release: 2%{?dist} +Release: 3%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -112,6 +112,7 @@ Patch44: xsa376.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch Patch47: xsa433-4.16.patch +Patch48: xsa433-bugfix.patch %if %build_qemutrad @@ -325,6 +326,7 @@ manage Xen virtual machines. %patch45 -p1 %patch46 -p1 %patch47 -p1 +%patch48 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -933,6 +935,9 @@ fi %endif %changelog +* Mon Jul 31 2023 Michael Young - 4.16.4-3 +- bugfix for x86/AMD: Zenbleed [XSA-433, CVE-2023-20593] + * Wed Jul 26 2023 Michael Young - 4.16.4-2 - x86/AMD: Zenbleed [XSA-433, CVE-2023-20593] diff --git a/xsa433-bugfix.patch b/xsa433-bugfix.patch new file mode 100644 index 0000000..8ad4eda --- /dev/null +++ b/xsa433-bugfix.patch @@ -0,0 +1,29 @@ +From: Andrew Cooper +Subject: x86/amd: Fix DE_CFG truncation in amd_check_zenbleed() + +This line: + + val &= ~chickenbit; + +ends up truncating val to 32 bits, and turning off various errata workarounds +in Zen2 systems. + +Fixes: f91c5ea97067 ("x86/amd: Mitigations for Zenbleed") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c +index 3ed06f670491..df2681b7c455 100644 +--- a/xen/arch/x86/cpu/amd.c ++++ b/xen/arch/x86/cpu/amd.c +@@ -909,8 +909,8 @@ void __init detect_zen2_null_seg_behaviour(void) + void amd_check_zenbleed(void) + { + const struct cpu_signature *sig = &this_cpu(cpu_sig); +- unsigned int good_rev, chickenbit = (1 << 9); +- uint64_t val, old_val; ++ unsigned int good_rev; ++ uint64_t val, old_val, chickenbit = (1 << 9); + + /* + * If we're virtualised, we can't do family/model checks safely, and From 5b38fd1c96f3839326b1ce546dc9c3d718ec99d6 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 1 Aug 2023 22:13:54 +0100 Subject: [PATCH 08/12] arm: Guests can trigger a deadlock on Cortex-A77 [XSA-436, CVE-2023-34320] --- xen.spec | 8 +- xsa436-4.16.patch | 288 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 295 insertions(+), 1 deletion(-) create mode 100644 xsa436-4.16.patch diff --git a/xen.spec b/xen.spec index 96a43c7..c692d17 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.4 -Release: 3%{?dist} +Release: 4%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -113,6 +113,7 @@ Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch Patch47: xsa433-4.16.patch Patch48: xsa433-bugfix.patch +Patch49: xsa436-4.16.patch %if %build_qemutrad @@ -327,6 +328,7 @@ manage Xen virtual machines. %patch46 -p1 %patch47 -p1 %patch48 -p1 +%patch49 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -935,6 +937,10 @@ fi %endif %changelog +* Tue Aug 01 2023 Michael Young - 4.16.4-4 +- arm: Guests can trigger a deadlock on Cortex-A77 [XSA-436, CVE-2023-34320] + (#2228238) + * Mon Jul 31 2023 Michael Young - 4.16.4-3 - bugfix for x86/AMD: Zenbleed [XSA-433, CVE-2023-20593] diff --git a/xsa436-4.16.patch b/xsa436-4.16.patch new file mode 100644 index 0000000..43819b3 --- /dev/null +++ b/xsa436-4.16.patch @@ -0,0 +1,288 @@ +From 5083b8b4a425c64820ac106ce0f79cc5af195b5b Mon Sep 17 00:00:00 2001 +From: Luca Fancellu +Date: Mon, 17 Jul 2023 13:25:46 +0100 +Subject: [PATCH] xen/arm: Add Cortex-A77 erratum 1508412 handling + +Cortex-A77 cores (r0p0, r1p0) could deadlock on a sequence of a +store-exclusive or read of PAR_EL1 and a load with device or non-cacheable +memory attributes. +A workaround is available, but it depends on a firmware counterpart. + +The proposed workaround from the errata document is to modify the software +running at EL1 and above to include a DMB SY before and after accessing +PAR_EL1. + +In conjunction to the above, the firmware needs to use a specific write +sequence to several IMPLEMENTATION DEFINED registers to have the hardware +insert a DMB SY after all load-exclusive and store-exclusive instructions. + +Apply the workaround to Xen where PAR_EL1 is read, implementing an helper +function to do that. +Since Xen can be interrupted by irqs in any moment, add a barrier on +entry/exit when we are running on the affected cores. + +A guest without the workaround can deadlock the system, so warn the users +of Xen with the above type of cores to use only trusted guests, by +printing a message on Xen startup. + +Signed-off-by: Luca Fancellu +Reviewed-by: Bertrand Marquis +Reviewed-by: Julien Grall +--- + SUPPORT.md | 2 ++ + docs/misc/arm/silicon-errata.txt | 1 + + xen/arch/arm/Kconfig | 21 +++++++++++++++++++++ + xen/arch/arm/arm64/entry.S | 19 +++++++++++++++++++ + xen/arch/arm/cpuerrata.c | 16 ++++++++++++---- + xen/arch/arm/domain.c | 2 +- + xen/include/asm-arm/arm64/page.h | 12 ++++++------ + xen/include/asm-arm/cpufeature.h | 3 ++- + xen/include/asm-arm/sysregs.h | 24 ++++++++++++++++++++++++ + 9 files changed, 88 insertions(+), 12 deletions(-) + +diff --git a/SUPPORT.md b/SUPPORT.md +index 85726102eab8..78a93159e3da 100644 +--- a/SUPPORT.md ++++ b/SUPPORT.md +@@ -39,8 +39,10 @@ supported in this document. + + Status: Supported + Status, Cortex A57 r0p0-r1p1: Supported, not security supported ++ Status, Cortex A77 r0p0-r1p0: Supported, not security supported + + For the Cortex A57 r0p0 - r1p1, see Errata 832075. ++For the Cortex A77 r0p0 - r1p0, see Errata 1508412. + + ## Host hardware support + +diff --git a/docs/misc/arm/silicon-errata.txt b/docs/misc/arm/silicon-errata.txt +index 1925d8fd4ee0..c4e82df53566 100644 +--- a/docs/misc/arm/silicon-errata.txt ++++ b/docs/misc/arm/silicon-errata.txt +@@ -58,4 +58,5 @@ stable hypervisors. + | ARM | Cortex-A76 | #1286807 | ARM64_ERRATUM_1286807 | + | ARM | Neoverse-N1 | #1165522 | N/A + | ARM | Neoverse-N1 | #1286807 | ARM64_ERRATUM_1286807 | ++| ARM | Cortex-A77 | #1508412 | ARM64_ERRATUM_1508412 | + | ARM | MMU-500 | #842869 | N/A | +diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig +index ecfa6822e4d3..c787a042a0a2 100644 +--- a/xen/arch/arm/Kconfig ++++ b/xen/arch/arm/Kconfig +@@ -278,6 +278,27 @@ config ARM64_ERRATUM_1286807 + + If unsure, say Y. + ++config ARM64_ERRATUM_1508412 ++ bool "Cortex-A77: 1508412: possible deadlock on sequence of NC/Device load and store exclusive or PAR read" ++ default y ++ depends on ARM_64 ++ help ++ This option adds a workaround for Arm Cortex-A77 erratum 1508412. ++ ++ Affected Cortex-A77 cores (r0p0, r1p0) could deadlock on a sequence ++ of a store-exclusive or read of PAR_EL1 and a load with device or ++ non-cacheable memory attributes. The workaround depends on a firmware ++ counterpart. ++ ++ Xen guests must also have the workaround implemented or they can ++ deadlock the system. ++ ++ Work around the issue by inserting DMB SY barriers around PAR_EL1 ++ register reads and warning Xen users. The DMB barrier is sufficient ++ to prevent a speculative PAR_EL1 read. ++ ++ If unsure, say Y. ++ + endmenu + + config ARM64_HARDEN_BRANCH_PREDICTOR +diff --git a/xen/arch/arm/arm64/entry.S b/xen/arch/arm/arm64/entry.S +index 95f1a9268419..95ff4e3e0517 100644 +--- a/xen/arch/arm/arm64/entry.S ++++ b/xen/arch/arm/arm64/entry.S +@@ -134,6 +134,16 @@ + * position on the stack before. + */ + .macro entry, hyp, compat, save_x0_x1=1 ++ ++ /* ++ * Ensure any PAR_EL1 reads complete, in case we were interrupted ++ * between the PAR_EL1 read and the memory barrier for the erratum ++ * 1508412 workaround. ++ */ ++ alternative_if ARM64_WORKAROUND_1508412 ++ dmb sy ++ alternative_else_nop_endif ++ + sub sp, sp, #(UREGS_SPSR_el1 - UREGS_LR) /* CPSR, PC, SP, LR */ + + .if \hyp == 0 /* Guest mode */ +@@ -492,6 +502,15 @@ return_from_trap: + + ldr lr, [sp], #(UREGS_SPSR_el1 - UREGS_LR) /* CPSR, PC, SP, LR */ + ++ /* ++ * Ensure any device/NC reads complete, in case we were interrupted ++ * between the memory barrier for the erratum 1508412 workaround and ++ * any PAR_EL1 read. ++ */ ++ alternative_if ARM64_WORKAROUND_1508412 ++ dmb sy ++ alternative_else_nop_endif ++ + eret + sb + +diff --git a/xen/arch/arm/cpuerrata.c b/xen/arch/arm/cpuerrata.c +index ae649d16ef02..ea680fac2e44 100644 +--- a/xen/arch/arm/cpuerrata.c ++++ b/xen/arch/arm/cpuerrata.c +@@ -668,6 +668,14 @@ static const struct arm_cpu_capabilities arm_errata[] = { + .capability = ARM64_WORKAROUND_AT_SPECULATE, + MIDR_ALL_VERSIONS(MIDR_CORTEX_A57), + }, ++#ifdef CONFIG_ARM64_ERRATUM_1508412 ++ { ++ /* Cortex-A77 r0p0 - r1p0 */ ++ .desc = "ARM erratum 1508412 (hypervisor portion)", ++ .capability = ARM64_WORKAROUND_1508412, ++ MIDR_RANGE(MIDR_CORTEX_A77, 0, 1), ++ }, ++#endif + { + /* Cortex-A55 (All versions as erratum is open in SDEN v14) */ + .desc = "ARM erratum 1530923", +@@ -686,11 +694,11 @@ void __init enable_errata_workarounds(void) + { + enable_cpu_capabilities(arm_errata); + +-#ifdef CONFIG_ARM64_ERRATUM_832075 +- if ( cpus_have_cap(ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE) ) ++#if defined(CONFIG_ARM64_ERRATUM_832075) || defined(CONFIG_ARM64_ERRATUM_1508412) ++ if ( cpus_have_cap(ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE) || ++ cpus_have_cap(ARM64_WORKAROUND_1508412) ) + { +- printk_once("**** This CPU is affected by the errata 832075. ****\n" +- "**** Guests without CPU erratum workarounds can deadlock the system! ****\n" ++ printk_once("**** Guests without CPU erratum workarounds can deadlock the system! ****\n" + "**** Only trusted guests should be used. ****\n"); + + /* Taint the machine has being insecure */ +diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c +index 96e1b235501d..cb1d54ba8549 100644 +--- a/xen/arch/arm/domain.c ++++ b/xen/arch/arm/domain.c +@@ -137,7 +137,7 @@ static void ctxt_switch_from(struct vcpu *p) + p->arch.ttbr1 = READ_SYSREG64(TTBR1_EL1); + if ( is_32bit_domain(p->domain) ) + p->arch.dacr = READ_SYSREG(DACR32_EL2); +- p->arch.par = READ_SYSREG64(PAR_EL1); ++ p->arch.par = read_sysreg_par(); + #if defined(CONFIG_ARM_32) + p->arch.mair0 = READ_CP32(MAIR0); + p->arch.mair1 = READ_CP32(MAIR1); +diff --git a/xen/include/asm-arm/arm64/page.h b/xen/include/asm-arm/arm64/page.h +index 0cba2663733b..fbfe67bf8951 100644 +--- a/xen/include/asm-arm/arm64/page.h ++++ b/xen/include/asm-arm/arm64/page.h +@@ -48,11 +48,11 @@ static inline void invalidate_icache_local(void) + /* Ask the MMU to translate a VA for us */ + static inline uint64_t __va_to_par(vaddr_t va) + { +- uint64_t par, tmp = READ_SYSREG64(PAR_EL1); ++ uint64_t par, tmp = read_sysreg_par(); + + asm volatile ("at s1e2r, %0;" : : "r" (va)); + isb(); +- par = READ_SYSREG64(PAR_EL1); ++ par = read_sysreg_par(); + WRITE_SYSREG64(tmp, PAR_EL1); + return par; + } +@@ -60,28 +60,28 @@ static inline uint64_t __va_to_par(vaddr_t va) + /* Ask the MMU to translate a Guest VA for us */ + static inline uint64_t gva_to_ma_par(vaddr_t va, unsigned int flags) + { +- uint64_t par, tmp = READ_SYSREG64(PAR_EL1); ++ uint64_t par, tmp = read_sysreg_par(); + + if ( (flags & GV2M_WRITE) == GV2M_WRITE ) + asm volatile ("at s12e1w, %0;" : : "r" (va)); + else + asm volatile ("at s12e1r, %0;" : : "r" (va)); + isb(); +- par = READ_SYSREG64(PAR_EL1); ++ par = read_sysreg_par(); + WRITE_SYSREG64(tmp, PAR_EL1); + return par; + } + + static inline uint64_t gva_to_ipa_par(vaddr_t va, unsigned int flags) + { +- uint64_t par, tmp = READ_SYSREG64(PAR_EL1); ++ uint64_t par, tmp = read_sysreg_par(); + + if ( (flags & GV2M_WRITE) == GV2M_WRITE ) + asm volatile ("at s1e1w, %0;" : : "r" (va)); + else + asm volatile ("at s1e1r, %0;" : : "r" (va)); + isb(); +- par = READ_SYSREG64(PAR_EL1); ++ par = read_sysreg_par(); + WRITE_SYSREG64(tmp, PAR_EL1); + return par; + } +diff --git a/xen/include/asm-arm/cpufeature.h b/xen/include/asm-arm/cpufeature.h +index f7368766c07c..2f38137f39d5 100644 +--- a/xen/include/asm-arm/cpufeature.h ++++ b/xen/include/asm-arm/cpufeature.h +@@ -67,8 +67,9 @@ + #define ARM_WORKAROUND_BHB_LOOP_24 13 + #define ARM_WORKAROUND_BHB_LOOP_32 14 + #define ARM_WORKAROUND_BHB_SMCC_3 15 ++#define ARM64_WORKAROUND_1508412 16 + +-#define ARM_NCAPS 16 ++#define ARM_NCAPS 17 + + #ifndef __ASSEMBLY__ + +diff --git a/xen/include/asm-arm/sysregs.h b/xen/include/asm-arm/sysregs.h +index 5c5c51bbcdbf..61e30c9e517c 100644 +--- a/xen/include/asm-arm/sysregs.h ++++ b/xen/include/asm-arm/sysregs.h +@@ -9,6 +9,30 @@ + # error "unknown ARM variant" + #endif + ++#ifndef __ASSEMBLY__ ++ ++#include ++ ++static inline register_t read_sysreg_par(void) ++{ ++ register_t par_el1; ++ ++ /* ++ * On Cortex-A77 r0p0 and r1p0, read access to PAR_EL1 shall include a ++ * DMB SY before and after accessing it, as part of the workaround for the ++ * errata 1508412. ++ */ ++ asm volatile(ALTERNATIVE("nop", "dmb sy", ARM64_WORKAROUND_1508412, ++ CONFIG_ARM64_ERRATUM_1508412)); ++ par_el1 = READ_SYSREG64(PAR_EL1); ++ asm volatile(ALTERNATIVE("nop", "dmb sy", ARM64_WORKAROUND_1508412, ++ CONFIG_ARM64_ERRATUM_1508412)); ++ ++ return par_el1; ++} ++ ++#endif /* !__ASSEMBLY__ */ ++ + #endif /* __ASM_ARM_SYSREGS_H */ + /* + * Local variables: +-- +2.40.1 + From 046e9e564a7f3d6b0f5566e4720050f35a30819e Mon Sep 17 00:00:00 2001 From: Michael Young Date: Fri, 11 Aug 2023 19:41:01 +0100 Subject: [PATCH 09/12] update to xen-4.16.5 which includes security updates x86/AMD: Speculative Return Stack Overflow [XSA-434, CVE-2023-20569] x86/Intel: Gather Data Sampling [XSA-435, CVE-2022-40982] remove patches now included upstream --- .gitignore | 2 +- sources | 2 +- xen.spec | 16 +-- xsa433-4.16.patch | 138 --------------------- xsa433-bugfix.patch | 29 ----- xsa436-4.16.patch | 288 -------------------------------------------- 6 files changed, 10 insertions(+), 465 deletions(-) delete mode 100644 xsa433-4.16.patch delete mode 100644 xsa433-bugfix.patch delete mode 100644 xsa436-4.16.patch diff --git a/.gitignore b/.gitignore index d252877..e8e5147 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.16.4.tar.gz +/xen-4.16.5.tar.gz diff --git a/sources b/sources index 55e15c7..b7ed437 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.16.4.tar.gz) = 1b5a167a9f5537b5311ef1cb56e0e6ffda0e4e569ef0534e47b4f4a528067bda46fb58b2a01abc02e8c38b2b70207fb201a5d4d10274fba70135eaa8e318e055 +SHA512 (xen-4.16.5.tar.gz) = 2f370787b72b2cd9d81c0b5f138133e676d2b9c8c76e31e6439649d7145242a6b7be0d51a7ff4f4197a99e3f6b24ac50e63d2fa49368da440d3f555e70c4ebd3 diff --git a/xen.spec b/xen.spec index c692d17..aeff813 100644 --- a/xen.spec +++ b/xen.spec @@ -54,8 +54,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.16.4 -Release: 4%{?dist} +Version: 4.16.5 +Release: 1%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -111,9 +111,6 @@ Patch43: xen.gcc11.fixes.patch Patch44: xsa376.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch -Patch47: xsa433-4.16.patch -Patch48: xsa433-bugfix.patch -Patch49: xsa436-4.16.patch %if %build_qemutrad @@ -326,9 +323,6 @@ manage Xen virtual machines. %patch44 -p1 %patch45 -p1 %patch46 -p1 -%patch47 -p1 -%patch48 -p1 -%patch49 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -937,6 +931,12 @@ fi %endif %changelog +* Fri Aug 11 2023 Michael Young - 4.16.5-1 +- update to xen-4.16.5 which includes + x86/AMD: Speculative Return Stack Overflow [XSA-434, CVE-2023-20569] + x86/Intel: Gather Data Sampling [XSA-435, CVE-2022-40982] +- remove patches now included upstream + * Tue Aug 01 2023 Michael Young - 4.16.4-4 - arm: Guests can trigger a deadlock on Cortex-A77 [XSA-436, CVE-2023-34320] (#2228238) diff --git a/xsa433-4.16.patch b/xsa433-4.16.patch deleted file mode 100644 index eab4204..0000000 --- a/xsa433-4.16.patch +++ /dev/null @@ -1,138 +0,0 @@ -From: Andrew Cooper -Subject: x86/amd: Mitigations for Zenbleed - -Zenbleed is a malfunction on AMD Zen2 uarch parts which results in corruption -of the vector registers. An attacker can trigger this bug deliberately in -order to access stale data in the physical vector register file. This can -include data from sibling threads, or a higher-privilege context. - -Microcode is the preferred mitigation but in the case that's not available use -the chickenbit as instructed by AMD. Re-evaluate the mitigation on late -microcode load too. - -This is XSA-433 / CVE-2023-20593. - -Signed-off-by: Andrew Cooper -Acked-by: Roger Pau Monné - -diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c -index 08e3e1e8a2d8..4ed08df4a8ce 100644 ---- a/xen/arch/x86/cpu/amd.c -+++ b/xen/arch/x86/cpu/amd.c -@@ -13,6 +13,7 @@ - #include - #include - #include -+#include - - #include "cpu.h" - -@@ -769,6 +770,72 @@ void __init detect_zen2_null_seg_behaviour(void) - - } - -+void amd_check_zenbleed(void) -+{ -+ const struct cpu_signature *sig = &this_cpu(cpu_sig); -+ unsigned int good_rev, chickenbit = (1 << 9); -+ uint64_t val, old_val; -+ -+ /* -+ * If we're virtualised, we can't do family/model checks safely, and -+ * we likely wouldn't have access to DE_CFG even if we could see a -+ * microcode revision. -+ * -+ * A hypervisor may hide AVX as a stopgap mitigation. We're not in a -+ * position to care either way. An admin doesn't want to be disabling -+ * AVX as a mitigation on any build of Xen with this logic present. -+ */ -+ if (cpu_has_hypervisor || boot_cpu_data.x86 != 0x17) -+ return; -+ -+ switch (boot_cpu_data.x86_model) { -+ case 0x30 ... 0x3f: good_rev = 0x0830107a; break; -+ case 0x60 ... 0x67: good_rev = 0x0860010b; break; -+ case 0x68 ... 0x6f: good_rev = 0x08608105; break; -+ case 0x70 ... 0x7f: good_rev = 0x08701032; break; -+ case 0xa0 ... 0xaf: good_rev = 0x08a00008; break; -+ default: -+ /* -+ * With the Fam17h check above, parts getting here are Zen1. -+ * They're not affected. -+ */ -+ return; -+ } -+ -+ rdmsrl(MSR_AMD64_DE_CFG, val); -+ old_val = val; -+ -+ /* -+ * Microcode is the preferred mitigation, in terms of performance. -+ * However, without microcode, this chickenbit (specific to the Zen2 -+ * uarch) disables Floating Point Mov-Elimination to mitigate the -+ * issue. -+ */ -+ val &= ~chickenbit; -+ if (sig->rev < good_rev) -+ val |= chickenbit; -+ -+ if (val == old_val) -+ /* Nothing to change. */ -+ return; -+ -+ /* -+ * DE_CFG is a Core-scoped MSR, and this write is racy during late -+ * microcode load. However, both threads calculate the new value from -+ * state which is shared, and unrelated to the old value, so the -+ * result should be consistent. -+ */ -+ wrmsrl(MSR_AMD64_DE_CFG, val); -+ -+ /* -+ * Inform the admin that we changed something, but don't spam, -+ * especially during a late microcode load. -+ */ -+ if (smp_processor_id() == 0) -+ printk(XENLOG_INFO "Zenbleed mitigation - using %s\n", -+ val & chickenbit ? "chickenbit" : "microcode"); -+} -+ - static void init_amd(struct cpuinfo_x86 *c) - { - u32 l, h; -@@ -1041,6 +1108,8 @@ static void init_amd(struct cpuinfo_x86 *c) - if ((smp_processor_id() == 1) && !cpu_has(c, X86_FEATURE_ITSC)) - disable_c1_ramping(); - -+ amd_check_zenbleed(); -+ - check_syscfg_dram_mod_en(); - - amd_log_freq(c); -diff --git a/xen/arch/x86/cpu/microcode/amd.c b/xen/arch/x86/cpu/microcode/amd.c -index 52182c1a2383..483a9b547f80 100644 ---- a/xen/arch/x86/cpu/microcode/amd.c -+++ b/xen/arch/x86/cpu/microcode/amd.c -@@ -262,6 +262,8 @@ static int apply_microcode(const struct microcode_patch *patch) - "microcode: CPU%u updated from revision %#x to %#x, date = %04x-%02x-%02x\n", - cpu, old_rev, rev, patch->year, patch->month, patch->day); - -+ amd_check_zenbleed(); -+ - return 0; - } - -diff --git a/xen/include/asm-x86/processor.h b/xen/include/asm-x86/processor.h -index 3d8aacd3aab2..96621ec39f8b 100644 ---- a/xen/include/asm-x86/processor.h -+++ b/xen/include/asm-x86/processor.h -@@ -639,6 +639,8 @@ enum ap_boot_method { - }; - extern enum ap_boot_method ap_boot_method; - -+void amd_check_zenbleed(void); -+ - #endif /* !__ASSEMBLY__ */ - - #endif /* __ASM_X86_PROCESSOR_H */ - diff --git a/xsa433-bugfix.patch b/xsa433-bugfix.patch deleted file mode 100644 index 8ad4eda..0000000 --- a/xsa433-bugfix.patch +++ /dev/null @@ -1,29 +0,0 @@ -From: Andrew Cooper -Subject: x86/amd: Fix DE_CFG truncation in amd_check_zenbleed() - -This line: - - val &= ~chickenbit; - -ends up truncating val to 32 bits, and turning off various errata workarounds -in Zen2 systems. - -Fixes: f91c5ea97067 ("x86/amd: Mitigations for Zenbleed") -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c -index 3ed06f670491..df2681b7c455 100644 ---- a/xen/arch/x86/cpu/amd.c -+++ b/xen/arch/x86/cpu/amd.c -@@ -909,8 +909,8 @@ void __init detect_zen2_null_seg_behaviour(void) - void amd_check_zenbleed(void) - { - const struct cpu_signature *sig = &this_cpu(cpu_sig); -- unsigned int good_rev, chickenbit = (1 << 9); -- uint64_t val, old_val; -+ unsigned int good_rev; -+ uint64_t val, old_val, chickenbit = (1 << 9); - - /* - * If we're virtualised, we can't do family/model checks safely, and diff --git a/xsa436-4.16.patch b/xsa436-4.16.patch deleted file mode 100644 index 43819b3..0000000 --- a/xsa436-4.16.patch +++ /dev/null @@ -1,288 +0,0 @@ -From 5083b8b4a425c64820ac106ce0f79cc5af195b5b Mon Sep 17 00:00:00 2001 -From: Luca Fancellu -Date: Mon, 17 Jul 2023 13:25:46 +0100 -Subject: [PATCH] xen/arm: Add Cortex-A77 erratum 1508412 handling - -Cortex-A77 cores (r0p0, r1p0) could deadlock on a sequence of a -store-exclusive or read of PAR_EL1 and a load with device or non-cacheable -memory attributes. -A workaround is available, but it depends on a firmware counterpart. - -The proposed workaround from the errata document is to modify the software -running at EL1 and above to include a DMB SY before and after accessing -PAR_EL1. - -In conjunction to the above, the firmware needs to use a specific write -sequence to several IMPLEMENTATION DEFINED registers to have the hardware -insert a DMB SY after all load-exclusive and store-exclusive instructions. - -Apply the workaround to Xen where PAR_EL1 is read, implementing an helper -function to do that. -Since Xen can be interrupted by irqs in any moment, add a barrier on -entry/exit when we are running on the affected cores. - -A guest without the workaround can deadlock the system, so warn the users -of Xen with the above type of cores to use only trusted guests, by -printing a message on Xen startup. - -Signed-off-by: Luca Fancellu -Reviewed-by: Bertrand Marquis -Reviewed-by: Julien Grall ---- - SUPPORT.md | 2 ++ - docs/misc/arm/silicon-errata.txt | 1 + - xen/arch/arm/Kconfig | 21 +++++++++++++++++++++ - xen/arch/arm/arm64/entry.S | 19 +++++++++++++++++++ - xen/arch/arm/cpuerrata.c | 16 ++++++++++++---- - xen/arch/arm/domain.c | 2 +- - xen/include/asm-arm/arm64/page.h | 12 ++++++------ - xen/include/asm-arm/cpufeature.h | 3 ++- - xen/include/asm-arm/sysregs.h | 24 ++++++++++++++++++++++++ - 9 files changed, 88 insertions(+), 12 deletions(-) - -diff --git a/SUPPORT.md b/SUPPORT.md -index 85726102eab8..78a93159e3da 100644 ---- a/SUPPORT.md -+++ b/SUPPORT.md -@@ -39,8 +39,10 @@ supported in this document. - - Status: Supported - Status, Cortex A57 r0p0-r1p1: Supported, not security supported -+ Status, Cortex A77 r0p0-r1p0: Supported, not security supported - - For the Cortex A57 r0p0 - r1p1, see Errata 832075. -+For the Cortex A77 r0p0 - r1p0, see Errata 1508412. - - ## Host hardware support - -diff --git a/docs/misc/arm/silicon-errata.txt b/docs/misc/arm/silicon-errata.txt -index 1925d8fd4ee0..c4e82df53566 100644 ---- a/docs/misc/arm/silicon-errata.txt -+++ b/docs/misc/arm/silicon-errata.txt -@@ -58,4 +58,5 @@ stable hypervisors. - | ARM | Cortex-A76 | #1286807 | ARM64_ERRATUM_1286807 | - | ARM | Neoverse-N1 | #1165522 | N/A - | ARM | Neoverse-N1 | #1286807 | ARM64_ERRATUM_1286807 | -+| ARM | Cortex-A77 | #1508412 | ARM64_ERRATUM_1508412 | - | ARM | MMU-500 | #842869 | N/A | -diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig -index ecfa6822e4d3..c787a042a0a2 100644 ---- a/xen/arch/arm/Kconfig -+++ b/xen/arch/arm/Kconfig -@@ -278,6 +278,27 @@ config ARM64_ERRATUM_1286807 - - If unsure, say Y. - -+config ARM64_ERRATUM_1508412 -+ bool "Cortex-A77: 1508412: possible deadlock on sequence of NC/Device load and store exclusive or PAR read" -+ default y -+ depends on ARM_64 -+ help -+ This option adds a workaround for Arm Cortex-A77 erratum 1508412. -+ -+ Affected Cortex-A77 cores (r0p0, r1p0) could deadlock on a sequence -+ of a store-exclusive or read of PAR_EL1 and a load with device or -+ non-cacheable memory attributes. The workaround depends on a firmware -+ counterpart. -+ -+ Xen guests must also have the workaround implemented or they can -+ deadlock the system. -+ -+ Work around the issue by inserting DMB SY barriers around PAR_EL1 -+ register reads and warning Xen users. The DMB barrier is sufficient -+ to prevent a speculative PAR_EL1 read. -+ -+ If unsure, say Y. -+ - endmenu - - config ARM64_HARDEN_BRANCH_PREDICTOR -diff --git a/xen/arch/arm/arm64/entry.S b/xen/arch/arm/arm64/entry.S -index 95f1a9268419..95ff4e3e0517 100644 ---- a/xen/arch/arm/arm64/entry.S -+++ b/xen/arch/arm/arm64/entry.S -@@ -134,6 +134,16 @@ - * position on the stack before. - */ - .macro entry, hyp, compat, save_x0_x1=1 -+ -+ /* -+ * Ensure any PAR_EL1 reads complete, in case we were interrupted -+ * between the PAR_EL1 read and the memory barrier for the erratum -+ * 1508412 workaround. -+ */ -+ alternative_if ARM64_WORKAROUND_1508412 -+ dmb sy -+ alternative_else_nop_endif -+ - sub sp, sp, #(UREGS_SPSR_el1 - UREGS_LR) /* CPSR, PC, SP, LR */ - - .if \hyp == 0 /* Guest mode */ -@@ -492,6 +502,15 @@ return_from_trap: - - ldr lr, [sp], #(UREGS_SPSR_el1 - UREGS_LR) /* CPSR, PC, SP, LR */ - -+ /* -+ * Ensure any device/NC reads complete, in case we were interrupted -+ * between the memory barrier for the erratum 1508412 workaround and -+ * any PAR_EL1 read. -+ */ -+ alternative_if ARM64_WORKAROUND_1508412 -+ dmb sy -+ alternative_else_nop_endif -+ - eret - sb - -diff --git a/xen/arch/arm/cpuerrata.c b/xen/arch/arm/cpuerrata.c -index ae649d16ef02..ea680fac2e44 100644 ---- a/xen/arch/arm/cpuerrata.c -+++ b/xen/arch/arm/cpuerrata.c -@@ -668,6 +668,14 @@ static const struct arm_cpu_capabilities arm_errata[] = { - .capability = ARM64_WORKAROUND_AT_SPECULATE, - MIDR_ALL_VERSIONS(MIDR_CORTEX_A57), - }, -+#ifdef CONFIG_ARM64_ERRATUM_1508412 -+ { -+ /* Cortex-A77 r0p0 - r1p0 */ -+ .desc = "ARM erratum 1508412 (hypervisor portion)", -+ .capability = ARM64_WORKAROUND_1508412, -+ MIDR_RANGE(MIDR_CORTEX_A77, 0, 1), -+ }, -+#endif - { - /* Cortex-A55 (All versions as erratum is open in SDEN v14) */ - .desc = "ARM erratum 1530923", -@@ -686,11 +694,11 @@ void __init enable_errata_workarounds(void) - { - enable_cpu_capabilities(arm_errata); - --#ifdef CONFIG_ARM64_ERRATUM_832075 -- if ( cpus_have_cap(ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE) ) -+#if defined(CONFIG_ARM64_ERRATUM_832075) || defined(CONFIG_ARM64_ERRATUM_1508412) -+ if ( cpus_have_cap(ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE) || -+ cpus_have_cap(ARM64_WORKAROUND_1508412) ) - { -- printk_once("**** This CPU is affected by the errata 832075. ****\n" -- "**** Guests without CPU erratum workarounds can deadlock the system! ****\n" -+ printk_once("**** Guests without CPU erratum workarounds can deadlock the system! ****\n" - "**** Only trusted guests should be used. ****\n"); - - /* Taint the machine has being insecure */ -diff --git a/xen/arch/arm/domain.c b/xen/arch/arm/domain.c -index 96e1b235501d..cb1d54ba8549 100644 ---- a/xen/arch/arm/domain.c -+++ b/xen/arch/arm/domain.c -@@ -137,7 +137,7 @@ static void ctxt_switch_from(struct vcpu *p) - p->arch.ttbr1 = READ_SYSREG64(TTBR1_EL1); - if ( is_32bit_domain(p->domain) ) - p->arch.dacr = READ_SYSREG(DACR32_EL2); -- p->arch.par = READ_SYSREG64(PAR_EL1); -+ p->arch.par = read_sysreg_par(); - #if defined(CONFIG_ARM_32) - p->arch.mair0 = READ_CP32(MAIR0); - p->arch.mair1 = READ_CP32(MAIR1); -diff --git a/xen/include/asm-arm/arm64/page.h b/xen/include/asm-arm/arm64/page.h -index 0cba2663733b..fbfe67bf8951 100644 ---- a/xen/include/asm-arm/arm64/page.h -+++ b/xen/include/asm-arm/arm64/page.h -@@ -48,11 +48,11 @@ static inline void invalidate_icache_local(void) - /* Ask the MMU to translate a VA for us */ - static inline uint64_t __va_to_par(vaddr_t va) - { -- uint64_t par, tmp = READ_SYSREG64(PAR_EL1); -+ uint64_t par, tmp = read_sysreg_par(); - - asm volatile ("at s1e2r, %0;" : : "r" (va)); - isb(); -- par = READ_SYSREG64(PAR_EL1); -+ par = read_sysreg_par(); - WRITE_SYSREG64(tmp, PAR_EL1); - return par; - } -@@ -60,28 +60,28 @@ static inline uint64_t __va_to_par(vaddr_t va) - /* Ask the MMU to translate a Guest VA for us */ - static inline uint64_t gva_to_ma_par(vaddr_t va, unsigned int flags) - { -- uint64_t par, tmp = READ_SYSREG64(PAR_EL1); -+ uint64_t par, tmp = read_sysreg_par(); - - if ( (flags & GV2M_WRITE) == GV2M_WRITE ) - asm volatile ("at s12e1w, %0;" : : "r" (va)); - else - asm volatile ("at s12e1r, %0;" : : "r" (va)); - isb(); -- par = READ_SYSREG64(PAR_EL1); -+ par = read_sysreg_par(); - WRITE_SYSREG64(tmp, PAR_EL1); - return par; - } - - static inline uint64_t gva_to_ipa_par(vaddr_t va, unsigned int flags) - { -- uint64_t par, tmp = READ_SYSREG64(PAR_EL1); -+ uint64_t par, tmp = read_sysreg_par(); - - if ( (flags & GV2M_WRITE) == GV2M_WRITE ) - asm volatile ("at s1e1w, %0;" : : "r" (va)); - else - asm volatile ("at s1e1r, %0;" : : "r" (va)); - isb(); -- par = READ_SYSREG64(PAR_EL1); -+ par = read_sysreg_par(); - WRITE_SYSREG64(tmp, PAR_EL1); - return par; - } -diff --git a/xen/include/asm-arm/cpufeature.h b/xen/include/asm-arm/cpufeature.h -index f7368766c07c..2f38137f39d5 100644 ---- a/xen/include/asm-arm/cpufeature.h -+++ b/xen/include/asm-arm/cpufeature.h -@@ -67,8 +67,9 @@ - #define ARM_WORKAROUND_BHB_LOOP_24 13 - #define ARM_WORKAROUND_BHB_LOOP_32 14 - #define ARM_WORKAROUND_BHB_SMCC_3 15 -+#define ARM64_WORKAROUND_1508412 16 - --#define ARM_NCAPS 16 -+#define ARM_NCAPS 17 - - #ifndef __ASSEMBLY__ - -diff --git a/xen/include/asm-arm/sysregs.h b/xen/include/asm-arm/sysregs.h -index 5c5c51bbcdbf..61e30c9e517c 100644 ---- a/xen/include/asm-arm/sysregs.h -+++ b/xen/include/asm-arm/sysregs.h -@@ -9,6 +9,30 @@ - # error "unknown ARM variant" - #endif - -+#ifndef __ASSEMBLY__ -+ -+#include -+ -+static inline register_t read_sysreg_par(void) -+{ -+ register_t par_el1; -+ -+ /* -+ * On Cortex-A77 r0p0 and r1p0, read access to PAR_EL1 shall include a -+ * DMB SY before and after accessing it, as part of the workaround for the -+ * errata 1508412. -+ */ -+ asm volatile(ALTERNATIVE("nop", "dmb sy", ARM64_WORKAROUND_1508412, -+ CONFIG_ARM64_ERRATUM_1508412)); -+ par_el1 = READ_SYSREG64(PAR_EL1); -+ asm volatile(ALTERNATIVE("nop", "dmb sy", ARM64_WORKAROUND_1508412, -+ CONFIG_ARM64_ERRATUM_1508412)); -+ -+ return par_el1; -+} -+ -+#endif /* !__ASSEMBLY__ */ -+ - #endif /* __ASM_ARM_SYSREGS_H */ - /* - * Local variables: --- -2.40.1 - From a65ac23041114531b5a203905a9d8cb7afe6c0f0 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 26 Sep 2023 16:50:31 +0100 Subject: [PATCH 10/12] 3 security issues - arm32: The cache may not be properly cleaned/invalidated [XSA-437, CVE-2023-34321] - top-level shadow reference dropped too early for 64-bit PV guests [XSA-438, CVE-2023-34322] - x86/AMD: Divide speculative information leak [XSA-439, CVE-2023-20588] --- xen.spec | 33 +- xsa437-4.16.patch | 110 +++++ xsa438-4.16.patch | 414 ++++++++++++++++++ ...enbleed-check-to-models-good-ucode-i.patch | 49 +++ ...x-confusion-between-SPEC_CTRL_EXIT_T.patch | 77 ++++ ...ld-DO_SPEC_CTRL_EXIT_TO_XEN-into-it-.patch | 88 ++++ ...rn-the-remaining-SPEC_CTRL_-ENTRY-EX.patch | 86 ++++ ...prove-all-SPEC_CTRL_-ENTER-EXIT-_-co.patch | 109 +++++ ...-restore_all_xen-to-hold-stack_end-i.patch | 77 ++++ ...the-IST-ness-of-an-entry-for-the-exi.patch | 112 +++++ ...rl-Issue-VERW-during-IST-exit-to-Xen.patch | 92 ++++ ...troduce-is_zen-1-2-_uarch-predicates.patch | 94 ++++ ...c-ctrl-Mitigate-the-Zen1-DIV-leakage.patch | 239 ++++++++++ 13 files changed, 1579 insertions(+), 1 deletion(-) create mode 100644 xsa437-4.16.patch create mode 100644 xsa438-4.16.patch create mode 100644 xsa439-0001-x86-AMD-extend-Zenbleed-check-to-models-good-ucode-i.patch create mode 100644 xsa439-0002-x86-spec-ctrl-Fix-confusion-between-SPEC_CTRL_EXIT_T.patch create mode 100644 xsa439-0003-x86-spec-ctrl-Fold-DO_SPEC_CTRL_EXIT_TO_XEN-into-it-.patch create mode 100644 xsa439-0004-x86-spec-ctrl-Turn-the-remaining-SPEC_CTRL_-ENTRY-EX.patch create mode 100644 xsa439-0005-x86-spec-ctrl-Improve-all-SPEC_CTRL_-ENTER-EXIT-_-co.patch create mode 100644 xsa439-0006-x86-entry-Adjust-restore_all_xen-to-hold-stack_end-i.patch create mode 100644 xsa439-0007-x86-entry-Track-the-IST-ness-of-an-entry-for-the-exi.patch create mode 100644 xsa439-0008-x86-spec-ctrl-Issue-VERW-during-IST-exit-to-Xen.patch create mode 100644 xsa439-0009-x86-amd-Introduce-is_zen-1-2-_uarch-predicates.patch create mode 100644 xsa439-0010-x86-spec-ctrl-Mitigate-the-Zen1-DIV-leakage.patch diff --git a/xen.spec b/xen.spec index aeff813..7c731d8 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.5 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -111,6 +111,18 @@ Patch43: xen.gcc11.fixes.patch Patch44: xsa376.patch Patch45: xen.gcc12.fixes.patch Patch46: xen.efi.build.patch +Patch47: xsa437-4.16.patch +Patch48: xsa438-4.16.patch +Patch49: xsa439-0001-x86-AMD-extend-Zenbleed-check-to-models-good-ucode-i.patch +Patch50: xsa439-0002-x86-spec-ctrl-Fix-confusion-between-SPEC_CTRL_EXIT_T.patch +Patch51: xsa439-0003-x86-spec-ctrl-Fold-DO_SPEC_CTRL_EXIT_TO_XEN-into-it-.patch +Patch52: xsa439-0004-x86-spec-ctrl-Turn-the-remaining-SPEC_CTRL_-ENTRY-EX.patch +Patch53: xsa439-0005-x86-spec-ctrl-Improve-all-SPEC_CTRL_-ENTER-EXIT-_-co.patch +Patch54: xsa439-0006-x86-entry-Adjust-restore_all_xen-to-hold-stack_end-i.patch +Patch55: xsa439-0007-x86-entry-Track-the-IST-ness-of-an-entry-for-the-exi.patch +Patch56: xsa439-0008-x86-spec-ctrl-Issue-VERW-during-IST-exit-to-Xen.patch +Patch57: xsa439-0009-x86-amd-Introduce-is_zen-1-2-_uarch-predicates.patch +Patch58: xsa439-0010-x86-spec-ctrl-Mitigate-the-Zen1-DIV-leakage.patch %if %build_qemutrad @@ -323,6 +335,18 @@ manage Xen virtual machines. %patch44 -p1 %patch45 -p1 %patch46 -p1 +%patch47 -p1 +%patch48 -p1 +%patch49 -p1 +%patch50 -p1 +%patch51 -p1 +%patch52 -p1 +%patch53 -p1 +%patch54 -p1 +%patch55 -p1 +%patch56 -p1 +%patch57 -p1 +%patch58 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -931,6 +955,13 @@ fi %endif %changelog +* Tue Sep 26 2023 Michael Young - 4.16.5-2 +- arm32: The cache may not be properly cleaned/invalidated [XSA-437, + CVE-2023-34321] +- top-level shadow reference dropped too early for 64-bit PV guests + [XSA-438, CVE-2023-34322] +- x86/AMD: Divide speculative information leak [XSA-439, CVE-2023-20588] + * Fri Aug 11 2023 Michael Young - 4.16.5-1 - update to xen-4.16.5 which includes x86/AMD: Speculative Return Stack Overflow [XSA-434, CVE-2023-20569] diff --git a/xsa437-4.16.patch b/xsa437-4.16.patch new file mode 100644 index 0000000..21cdb4a --- /dev/null +++ b/xsa437-4.16.patch @@ -0,0 +1,110 @@ +From 2b21319eecd8623078f27f0bccbbfdeb26606ff1 Mon Sep 17 00:00:00 2001 +From: Stefano Stabellini +Date: Thu, 17 Aug 2023 13:41:35 +0100 +Subject: [PATCH] xen/arm: page: Handle cache flush of an element at the top of + the address space + +The region that needs to be cleaned/invalidated may be at the top +of the address space. This means that 'end' (i.e. 'p + size') will +be 0 and therefore nothing will be cleaned/invalidated as the check +in the loop will always be false. + +On Arm64, we only support we only support up to 48-bit Virtual +address space. So this is not a concern there. However, for 32-bit, +the mapcache is using the last 2GB of the address space. Therefore +we may not clean/invalidate properly some pages. This could lead +to memory corruption or data leakage (the scrubbed value may +still sit in the cache when the guest could read directly the memory +and therefore read the old content). + +Rework invalidate_dcache_va_range(), clean_dcache_va_range(), +clean_and_invalidate_dcache_va_range() to handle a cache flush +with an element at the top of the address space. + +This is CVE-2023-34321 / XSA-437. + +Reported-by: Julien Grall +Signed-off-by: Stefano Stabellini +Signed-off-by: Julien Grall +Acked-by: Bertrand Marquis + +--- + xen/include/asm-arm/page.h | 33 ++++++++++++++++++++------------- + 1 file changed, 20 insertions(+), 13 deletions(-) + +diff --git a/xen/include/asm-arm/page.h b/xen/include/asm-arm/page.h +index c6f9fb0d4e0c..eff5883ef87b 100644 +--- a/xen/include/asm-arm/page.h ++++ b/xen/include/asm-arm/page.h +@@ -152,26 +152,25 @@ static inline size_t read_dcache_line_bytes(void) + + static inline int invalidate_dcache_va_range(const void *p, unsigned long size) + { +- const void *end = p + size; + size_t cacheline_mask = dcache_line_bytes - 1; + + dsb(sy); /* So the CPU issues all writes to the range */ + + if ( (uintptr_t)p & cacheline_mask ) + { ++ size -= dcache_line_bytes - ((uintptr_t)p & cacheline_mask); + p = (void *)((uintptr_t)p & ~cacheline_mask); + asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p)); + p += dcache_line_bytes; + } +- if ( (uintptr_t)end & cacheline_mask ) +- { +- end = (void *)((uintptr_t)end & ~cacheline_mask); +- asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (end)); +- } + +- for ( ; p < end; p += dcache_line_bytes ) ++ for ( ; size >= dcache_line_bytes; ++ p += dcache_line_bytes, size -= dcache_line_bytes ) + asm volatile (__invalidate_dcache_one(0) : : "r" (p)); + ++ if ( size > 0 ) ++ asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p)); ++ + dsb(sy); /* So we know the flushes happen before continuing */ + + return 0; +@@ -179,10 +178,14 @@ static inline int invalidate_dcache_va_range(const void *p, unsigned long size) + + static inline int clean_dcache_va_range(const void *p, unsigned long size) + { +- const void *end = p + size; ++ size_t cacheline_mask = dcache_line_bytes - 1; ++ + dsb(sy); /* So the CPU issues all writes to the range */ +- p = (void *)((uintptr_t)p & ~(dcache_line_bytes - 1)); +- for ( ; p < end; p += dcache_line_bytes ) ++ size += (uintptr_t)p & cacheline_mask; ++ size = (size + cacheline_mask) & ~cacheline_mask; ++ p = (void *)((uintptr_t)p & ~cacheline_mask); ++ for ( ; size >= dcache_line_bytes; ++ p += dcache_line_bytes, size -= dcache_line_bytes ) + asm volatile (__clean_dcache_one(0) : : "r" (p)); + dsb(sy); /* So we know the flushes happen before continuing */ + /* ARM callers assume that dcache_* functions cannot fail. */ +@@ -192,10 +195,14 @@ static inline int clean_dcache_va_range(const void *p, unsigned long size) + static inline int clean_and_invalidate_dcache_va_range + (const void *p, unsigned long size) + { +- const void *end = p + size; ++ size_t cacheline_mask = dcache_line_bytes - 1; ++ + dsb(sy); /* So the CPU issues all writes to the range */ +- p = (void *)((uintptr_t)p & ~(dcache_line_bytes - 1)); +- for ( ; p < end; p += dcache_line_bytes ) ++ size += (uintptr_t)p & cacheline_mask; ++ size = (size + cacheline_mask) & ~cacheline_mask; ++ p = (void *)((uintptr_t)p & ~cacheline_mask); ++ for ( ; size >= dcache_line_bytes; ++ p += dcache_line_bytes, size -= dcache_line_bytes ) + asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p)); + dsb(sy); /* So we know the flushes happen before continuing */ + /* ARM callers assume that dcache_* functions cannot fail. */ +-- +2.40.1 + diff --git a/xsa438-4.16.patch b/xsa438-4.16.patch new file mode 100644 index 0000000..f6817a3 --- /dev/null +++ b/xsa438-4.16.patch @@ -0,0 +1,414 @@ +From: Jan Beulich +Subject: x86/shadow: defer releasing of PV's top-level shadow reference + +sh_set_toplevel_shadow() re-pinning the top-level shadow we may be +running on is not enough (and at the same time unnecessary when the +shadow isn't what we're running on): That shadow becomes eligible for +blowing away (from e.g. shadow_prealloc()) immediately after the +paging lock was dropped. Yet it needs to remain valid until the actual +page table switch occurred. + +Propagate up the call chain the shadow entry that needs releasing +eventually, and carry out the release immediately after switching page +tables. Handle update_cr3() failures by switching to idle pagetables. +Note that various further uses of update_cr3() are HVM-only or only act +on paused vCPU-s, in which case sh_set_toplevel_shadow() will not defer +releasing of the reference. + +While changing the update_cr3() hook, also convert the "do_locking" +parameter to boolean. + +This is CVE-2023-34322 / XSA-438. + +Reported-by: Tim Deegan +Signed-off-by: Jan Beulich +Reviewed-by: George Dunlap + +--- a/xen/arch/x86/mm/hap/hap.c ++++ b/xen/arch/x86/mm/hap/hap.c +@@ -728,10 +728,12 @@ static bool_t hap_invlpg(struct vcpu *v, + return 1; + } + +-static void hap_update_cr3(struct vcpu *v, int do_locking, bool noflush) ++static pagetable_t hap_update_cr3(struct vcpu *v, bool do_locking, bool noflush) + { + v->arch.hvm.hw_cr[3] = v->arch.hvm.guest_cr[3]; + hvm_update_guest_cr3(v, noflush); ++ ++ return pagetable_null(); + } + + /* +--- a/xen/arch/x86/mm/shadow/common.c ++++ b/xen/arch/x86/mm/shadow/common.c +@@ -2574,13 +2574,13 @@ void shadow_update_paging_modes(struct v + } + + /* Set up the top-level shadow and install it in slot 'slot' of shadow_table */ +-void sh_set_toplevel_shadow(struct vcpu *v, +- unsigned int slot, +- mfn_t gmfn, +- unsigned int root_type, +- mfn_t (*make_shadow)(struct vcpu *v, +- mfn_t gmfn, +- uint32_t shadow_type)) ++pagetable_t sh_set_toplevel_shadow(struct vcpu *v, ++ unsigned int slot, ++ mfn_t gmfn, ++ unsigned int root_type, ++ mfn_t (*make_shadow)(struct vcpu *v, ++ mfn_t gmfn, ++ uint32_t shadow_type)) + { + mfn_t smfn; + pagetable_t old_entry, new_entry; +@@ -2637,20 +2637,37 @@ void sh_set_toplevel_shadow(struct vcpu + mfn_x(gmfn), mfn_x(pagetable_get_mfn(new_entry))); + v->arch.paging.shadow.shadow_table[slot] = new_entry; + +- /* Decrement the refcount of the old contents of this slot */ +- if ( !pagetable_is_null(old_entry) ) ++ /* ++ * Decrement the refcount of the old contents of this slot, unless ++ * we're still running on that shadow - in that case it'll need holding ++ * on to until the actual page table switch did occur. ++ */ ++ if ( !pagetable_is_null(old_entry) && (v != current || !is_pv_domain(d)) ) + { +- mfn_t old_smfn = pagetable_get_mfn(old_entry); +- /* Need to repin the old toplevel shadow if it's been unpinned +- * by shadow_prealloc(): in PV mode we're still running on this +- * shadow and it's not safe to free it yet. */ +- if ( !mfn_to_page(old_smfn)->u.sh.pinned && !sh_pin(d, old_smfn) ) +- { +- printk(XENLOG_G_ERR "can't re-pin %"PRI_mfn"\n", mfn_x(old_smfn)); +- domain_crash(d); +- } +- sh_put_ref(d, old_smfn, 0); ++ sh_put_ref(d, pagetable_get_mfn(old_entry), 0); ++ old_entry = pagetable_null(); + } ++ ++ /* ++ * 2- and 3-level shadow mode is used for HVM only. Therefore we never run ++ * on such a shadow, so only call sites requesting an L4 shadow need to pay ++ * attention to the returned value. ++ */ ++ ASSERT(pagetable_is_null(old_entry) || root_type == SH_type_l4_64_shadow); ++ ++ return old_entry; ++} ++ ++/* ++ * Helper invoked when releasing of a top-level shadow's reference was ++ * deferred in sh_set_toplevel_shadow() above. ++ */ ++void shadow_put_top_level(struct domain *d, pagetable_t old_entry) ++{ ++ ASSERT(!pagetable_is_null(old_entry)); ++ paging_lock(d); ++ sh_put_ref(d, pagetable_get_mfn(old_entry), 0); ++ paging_unlock(d); + } + + /**************************************************************************/ +--- a/xen/arch/x86/mm/shadow/multi.c ++++ b/xen/arch/x86/mm/shadow/multi.c +@@ -3219,8 +3219,8 @@ sh_detach_old_tables(struct vcpu *v) + } + } + +-static void +-sh_update_cr3(struct vcpu *v, int do_locking, bool noflush) ++static pagetable_t ++sh_update_cr3(struct vcpu *v, bool do_locking, bool noflush) + /* Updates vcpu->arch.cr3 after the guest has changed CR3. + * Paravirtual guests should set v->arch.guest_table (and guest_table_user, + * if appropriate). +@@ -3234,6 +3234,7 @@ sh_update_cr3(struct vcpu *v, int do_loc + { + struct domain *d = v->domain; + mfn_t gmfn; ++ pagetable_t old_entry = pagetable_null(); + #if GUEST_PAGING_LEVELS == 3 && defined(CONFIG_HVM) + const guest_l3e_t *gl3e; + unsigned int i, guest_idx; +@@ -3243,7 +3244,7 @@ sh_update_cr3(struct vcpu *v, int do_loc + if ( !is_hvm_domain(d) && !v->is_initialised ) + { + ASSERT(v->arch.cr3 == 0); +- return; ++ return old_entry; + } + + if ( do_locking ) paging_lock(v->domain); +@@ -3316,11 +3317,12 @@ sh_update_cr3(struct vcpu *v, int do_loc + #if GUEST_PAGING_LEVELS == 4 + if ( sh_remove_write_access(d, gmfn, 4, 0) != 0 ) + guest_flush_tlb_mask(d, d->dirty_cpumask); +- sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l4_shadow, sh_make_shadow); ++ old_entry = sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l4_shadow, ++ sh_make_shadow); + if ( unlikely(pagetable_is_null(v->arch.paging.shadow.shadow_table[0])) ) + { + ASSERT(d->is_dying || d->is_shutting_down); +- return; ++ return old_entry; + } + if ( !shadow_mode_external(d) && !is_pv_32bit_domain(d) ) + { +@@ -3366,24 +3368,30 @@ sh_update_cr3(struct vcpu *v, int do_loc + gl2gfn = guest_l3e_get_gfn(gl3e[i]); + gl2mfn = get_gfn_query_unlocked(d, gfn_x(gl2gfn), &p2mt); + if ( p2m_is_ram(p2mt) ) +- sh_set_toplevel_shadow(v, i, gl2mfn, SH_type_l2_shadow, +- sh_make_shadow); ++ old_entry = sh_set_toplevel_shadow(v, i, gl2mfn, ++ SH_type_l2_shadow, ++ sh_make_shadow); + else +- sh_set_toplevel_shadow(v, i, INVALID_MFN, 0, +- sh_make_shadow); ++ old_entry = sh_set_toplevel_shadow(v, i, INVALID_MFN, 0, ++ sh_make_shadow); + } + else +- sh_set_toplevel_shadow(v, i, INVALID_MFN, 0, sh_make_shadow); ++ old_entry = sh_set_toplevel_shadow(v, i, INVALID_MFN, 0, ++ sh_make_shadow); ++ ++ ASSERT(pagetable_is_null(old_entry)); + } + } + #elif GUEST_PAGING_LEVELS == 2 + if ( sh_remove_write_access(d, gmfn, 2, 0) != 0 ) + guest_flush_tlb_mask(d, d->dirty_cpumask); +- sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l2_shadow, sh_make_shadow); ++ old_entry = sh_set_toplevel_shadow(v, 0, gmfn, SH_type_l2_shadow, ++ sh_make_shadow); ++ ASSERT(pagetable_is_null(old_entry)); + if ( unlikely(pagetable_is_null(v->arch.paging.shadow.shadow_table[0])) ) + { + ASSERT(d->is_dying || d->is_shutting_down); +- return; ++ return old_entry; + } + #else + #error This should never happen +@@ -3477,6 +3485,8 @@ sh_update_cr3(struct vcpu *v, int do_loc + + /* Release the lock, if we took it (otherwise it's the caller's problem) */ + if ( do_locking ) paging_unlock(v->domain); ++ ++ return old_entry; + } + + +--- a/xen/arch/x86/mm/shadow/none.c ++++ b/xen/arch/x86/mm/shadow/none.c +@@ -52,9 +52,10 @@ static unsigned long _gva_to_gfn(struct + } + #endif + +-static void _update_cr3(struct vcpu *v, int do_locking, bool noflush) ++static pagetable_t _update_cr3(struct vcpu *v, bool do_locking, bool noflush) + { + ASSERT_UNREACHABLE(); ++ return pagetable_null(); + } + + static void _update_paging_modes(struct vcpu *v) +--- a/xen/arch/x86/mm/shadow/private.h ++++ b/xen/arch/x86/mm/shadow/private.h +@@ -391,13 +391,13 @@ mfn_t shadow_alloc(struct domain *d, + void shadow_free(struct domain *d, mfn_t smfn); + + /* Set up the top-level shadow and install it in slot 'slot' of shadow_table */ +-void sh_set_toplevel_shadow(struct vcpu *v, +- unsigned int slot, +- mfn_t gmfn, +- unsigned int root_type, +- mfn_t (*make_shadow)(struct vcpu *v, +- mfn_t gmfn, +- uint32_t shadow_type)); ++pagetable_t sh_set_toplevel_shadow(struct vcpu *v, ++ unsigned int slot, ++ mfn_t gmfn, ++ unsigned int root_type, ++ mfn_t (*make_shadow)(struct vcpu *v, ++ mfn_t gmfn, ++ uint32_t shadow_type)); + + /* Update the shadows in response to a pagetable write from Xen */ + int sh_validate_guest_entry(struct vcpu *v, mfn_t gmfn, void *entry, u32 size); +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -565,15 +565,12 @@ void write_ptbase(struct vcpu *v) + * + * Update ref counts to shadow tables appropriately. + */ +-void update_cr3(struct vcpu *v) ++pagetable_t update_cr3(struct vcpu *v) + { + mfn_t cr3_mfn; + + if ( paging_mode_enabled(v->domain) ) +- { +- paging_update_cr3(v, false); +- return; +- } ++ return paging_update_cr3(v, false); + + if ( !(v->arch.flags & TF_kernel_mode) ) + cr3_mfn = pagetable_get_mfn(v->arch.guest_table_user); +@@ -581,6 +578,8 @@ void update_cr3(struct vcpu *v) + cr3_mfn = pagetable_get_mfn(v->arch.guest_table); + + make_cr3(v, cr3_mfn); ++ ++ return pagetable_null(); + } + + static inline void set_tlbflush_timestamp(struct page_info *page) +@@ -3254,6 +3253,7 @@ int new_guest_cr3(mfn_t mfn) + struct domain *d = curr->domain; + int rc; + mfn_t old_base_mfn; ++ pagetable_t old_shadow; + + if ( is_pv_32bit_domain(d) ) + { +@@ -3321,9 +3321,22 @@ int new_guest_cr3(mfn_t mfn) + if ( !VM_ASSIST(d, m2p_strict) ) + fill_ro_mpt(mfn); + curr->arch.guest_table = pagetable_from_mfn(mfn); +- update_cr3(curr); ++ old_shadow = update_cr3(curr); ++ ++ /* ++ * In shadow mode update_cr3() can fail, in which case here we're still ++ * running on the prior top-level shadow (which we're about to release). ++ * Switch to the idle page tables in such an event; the guest will have ++ * been crashed already. ++ */ ++ if ( likely(!mfn_eq(pagetable_get_mfn(old_shadow), ++ maddr_to_mfn(curr->arch.cr3 & ~X86_CR3_NOFLUSH))) ) ++ write_ptbase(curr); ++ else ++ write_ptbase(idle_vcpu[curr->processor]); + +- write_ptbase(curr); ++ if ( !pagetable_is_null(old_shadow) ) ++ shadow_put_top_level(d, old_shadow); + + if ( likely(mfn_x(old_base_mfn) != 0) ) + { +--- a/xen/arch/x86/pv/domain.c ++++ b/xen/arch/x86/pv/domain.c +@@ -424,10 +424,13 @@ bool __init xpti_pcid_enabled(void) + + static void _toggle_guest_pt(struct vcpu *v) + { ++ bool guest_update; ++ pagetable_t old_shadow; + unsigned long cr3; + + v->arch.flags ^= TF_kernel_mode; +- update_cr3(v); ++ guest_update = v->arch.flags & TF_kernel_mode; ++ old_shadow = update_cr3(v); + + /* + * Don't flush user global mappings from the TLB. Don't tick TLB clock. +@@ -436,13 +439,31 @@ static void _toggle_guest_pt(struct vcpu + * TLB flush (for just the incoming PCID), as the top level page table may + * have changed behind our backs. To be on the safe side, suppress the + * no-flush unconditionally in this case. ++ * ++ * Furthermore in shadow mode update_cr3() can fail, in which case here ++ * we're still running on the prior top-level shadow (which we're about ++ * to release). Switch to the idle page tables in such an event; the ++ * guest will have been crashed already. + */ + cr3 = v->arch.cr3; + if ( shadow_mode_enabled(v->domain) ) ++ { + cr3 &= ~X86_CR3_NOFLUSH; ++ ++ if ( unlikely(mfn_eq(pagetable_get_mfn(old_shadow), ++ maddr_to_mfn(cr3))) ) ++ { ++ cr3 = idle_vcpu[v->processor]->arch.cr3; ++ /* Also suppress runstate/time area updates below. */ ++ guest_update = false; ++ } ++ } + write_cr3(cr3); + +- if ( !(v->arch.flags & TF_kernel_mode) ) ++ if ( !pagetable_is_null(old_shadow) ) ++ shadow_put_top_level(v->domain, old_shadow); ++ ++ if ( !guest_update ) + return; + + if ( v->arch.pv.need_update_runstate_area && update_runstate_area(v) ) +--- a/xen/include/asm-x86/mm.h ++++ b/xen/include/asm-x86/mm.h +@@ -564,7 +564,7 @@ void audit_domains(void); + #endif + + void make_cr3(struct vcpu *v, mfn_t mfn); +-void update_cr3(struct vcpu *v); ++pagetable_t update_cr3(struct vcpu *v); + int vcpu_destroy_pagetables(struct vcpu *); + void *do_page_walk(struct vcpu *v, unsigned long addr); + +--- a/xen/include/asm-x86/paging.h ++++ b/xen/include/asm-x86/paging.h +@@ -138,7 +138,7 @@ struct paging_mode { + paddr_t ga, uint32_t *pfec, + unsigned int *page_order); + #endif +- void (*update_cr3 )(struct vcpu *v, int do_locking, ++ pagetable_t (*update_cr3 )(struct vcpu *v, bool do_locking, + bool noflush); + void (*update_paging_modes )(struct vcpu *v); + bool (*flush_tlb )(bool (*flush_vcpu)(void *ctxt, +@@ -315,9 +315,9 @@ static inline unsigned long paging_ga_to + /* Update all the things that are derived from the guest's CR3. + * Called when the guest changes CR3; the caller can then use v->arch.cr3 + * as the value to load into the host CR3 to schedule this vcpu */ +-static inline void paging_update_cr3(struct vcpu *v, bool noflush) ++static inline pagetable_t paging_update_cr3(struct vcpu *v, bool noflush) + { +- paging_get_hostmode(v)->update_cr3(v, 1, noflush); ++ return paging_get_hostmode(v)->update_cr3(v, 1, noflush); + } + + /* Update all the things that are derived from the guest's CR0/CR3/CR4. +--- a/xen/include/asm-x86/shadow.h ++++ b/xen/include/asm-x86/shadow.h +@@ -97,6 +97,9 @@ void shadow_blow_tables_per_domain(struc + int shadow_set_allocation(struct domain *d, unsigned int pages, + bool *preempted); + ++/* Helper to invoke for deferred releasing of a top-level shadow's reference. */ ++void shadow_put_top_level(struct domain *d, pagetable_t old); ++ + #else /* !CONFIG_SHADOW_PAGING */ + + #define shadow_vcpu_teardown(v) ASSERT(is_pv_vcpu(v)) +@@ -118,6 +121,11 @@ static inline void shadow_prepare_page_t + + static inline void shadow_blow_tables_per_domain(struct domain *d) {} + ++static inline void shadow_put_top_level(struct domain *d, pagetable_t old) ++{ ++ ASSERT_UNREACHABLE(); ++} ++ + static inline int shadow_domctl(struct domain *d, + struct xen_domctl_shadow_op *sc, + XEN_GUEST_HANDLE_PARAM(xen_domctl_t) u_domctl) diff --git a/xsa439-0001-x86-AMD-extend-Zenbleed-check-to-models-good-ucode-i.patch b/xsa439-0001-x86-AMD-extend-Zenbleed-check-to-models-good-ucode-i.patch new file mode 100644 index 0000000..a01c726 --- /dev/null +++ b/xsa439-0001-x86-AMD-extend-Zenbleed-check-to-models-good-ucode-i.patch @@ -0,0 +1,49 @@ +From 08539e8315fdae5f5bfd655d53ed35fd2922fe6c Mon Sep 17 00:00:00 2001 +Message-ID: <08539e8315fdae5f5bfd655d53ed35fd2922fe6c.1695742580.git.m.a.young@durham.ac.uk> +From: Jan Beulich +Date: Wed, 23 Aug 2023 09:26:36 +0200 +Subject: [XEN PATCH 01/10] x86/AMD: extend Zenbleed check to models "good" + ucode isn't known for + +Reportedly the AMD Custom APU 0405 found on SteamDeck, models 0x90 and +0x91, (quoting the respective Linux commit) is similarly affected. Put +another instance of our Zen1 vs Zen2 distinction checks in +amd_check_zenbleed(), forcing use of the chickenbit irrespective of +ucode version (building upon real hardware never surfacing a version of +0xffffffff). + +Signed-off-by: Jan Beulich +Reviewed-by: Andrew Cooper +(cherry picked from commit 145a69c0944ac70cfcf9d247c85dee9e99d9d302) +--- + xen/arch/x86/cpu/amd.c | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) + +diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c +index 60c6d88edf..a591038757 100644 +--- a/xen/arch/x86/cpu/amd.c ++++ b/xen/arch/x86/cpu/amd.c +@@ -800,10 +800,17 @@ void amd_check_zenbleed(void) + case 0xa0 ... 0xaf: good_rev = 0x08a00008; break; + default: + /* +- * With the Fam17h check above, parts getting here are Zen1. +- * They're not affected. ++ * With the Fam17h check above, most parts getting here are ++ * Zen1. They're not affected. Assume Zen2 ones making it ++ * here are affected regardless of microcode version. ++ * ++ * Zen1 vs Zen2 isn't a simple model number comparison, so use ++ * STIBP as a heuristic to distinguish. + */ +- return; ++ if (!boot_cpu_has(X86_FEATURE_AMD_STIBP)) ++ return; ++ good_rev = ~0U; ++ break; + } + + rdmsrl(MSR_AMD64_DE_CFG, val); +-- +2.41.0 + diff --git a/xsa439-0002-x86-spec-ctrl-Fix-confusion-between-SPEC_CTRL_EXIT_T.patch b/xsa439-0002-x86-spec-ctrl-Fix-confusion-between-SPEC_CTRL_EXIT_T.patch new file mode 100644 index 0000000..edff254 --- /dev/null +++ b/xsa439-0002-x86-spec-ctrl-Fix-confusion-between-SPEC_CTRL_EXIT_T.patch @@ -0,0 +1,77 @@ +From 1e52cdf07cdf52e5d99957c3ecbddf5b1feda963 Mon Sep 17 00:00:00 2001 +Message-ID: <1e52cdf07cdf52e5d99957c3ecbddf5b1feda963.1695742580.git.m.a.young@durham.ac.uk> +In-Reply-To: <08539e8315fdae5f5bfd655d53ed35fd2922fe6c.1695742580.git.m.a.young@durham.ac.uk> +References: <08539e8315fdae5f5bfd655d53ed35fd2922fe6c.1695742580.git.m.a.young@durham.ac.uk> +From: Andrew Cooper +Date: Tue, 12 Sep 2023 15:06:49 +0100 +Subject: [XEN PATCH 02/10] x86/spec-ctrl: Fix confusion between + SPEC_CTRL_EXIT_TO_XEN{,_IST} + +c/s 3fffaf9c13e9 ("x86/entry: Avoid using alternatives in NMI/#MC paths") +dropped the only user, leaving behind the (incorrect) implication that Xen had +split exit paths. + +Delete the unused SPEC_CTRL_EXIT_TO_XEN and rename SPEC_CTRL_EXIT_TO_XEN_IST +to SPEC_CTRL_EXIT_TO_XEN for consistency. + +No functional change. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 1c18d73774533a55ba9d1cbee8bdace03efdb5e7) +--- + xen/arch/x86/x86_64/entry.S | 2 +- + xen/include/asm-x86/spec_ctrl_asm.h | 10 ++-------- + 2 files changed, 3 insertions(+), 9 deletions(-) + +diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S +index db2ea7871e..59f2040787 100644 +--- a/xen/arch/x86/x86_64/entry.S ++++ b/xen/arch/x86/x86_64/entry.S +@@ -664,7 +664,7 @@ UNLIKELY_START(ne, exit_cr3) + UNLIKELY_END(exit_cr3) + + /* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */ +- SPEC_CTRL_EXIT_TO_XEN_IST /* Req: %rbx=end, Clob: acd */ ++ SPEC_CTRL_EXIT_TO_XEN /* Req: %rbx=end, Clob: acd */ + + RESTORE_ALL adj=8 + iretq +diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h +index b61a5571ae..f5110616e4 100644 +--- a/xen/include/asm-x86/spec_ctrl_asm.h ++++ b/xen/include/asm-x86/spec_ctrl_asm.h +@@ -79,7 +79,6 @@ + * - SPEC_CTRL_ENTRY_FROM_PV + * - SPEC_CTRL_ENTRY_FROM_INTR + * - SPEC_CTRL_ENTRY_FROM_INTR_IST +- * - SPEC_CTRL_EXIT_TO_XEN_IST + * - SPEC_CTRL_EXIT_TO_XEN + * - SPEC_CTRL_EXIT_TO_PV + * +@@ -273,11 +272,6 @@ + ALTERNATIVE "", __stringify(DO_SPEC_CTRL_ENTRY maybexen=1), \ + X86_FEATURE_SC_MSR_PV + +-/* Use when exiting to Xen context. */ +-#define SPEC_CTRL_EXIT_TO_XEN \ +- ALTERNATIVE "", \ +- DO_SPEC_CTRL_EXIT_TO_XEN, X86_FEATURE_SC_MSR_PV +- + /* Use when exiting to PV guest context. */ + #define SPEC_CTRL_EXIT_TO_PV \ + ALTERNATIVE "", \ +@@ -344,8 +338,8 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + UNLIKELY_END(\@_serialise) + .endm + +-/* Use when exiting to Xen in IST context. */ +-.macro SPEC_CTRL_EXIT_TO_XEN_IST ++/* Use when exiting to Xen context. */ ++.macro SPEC_CTRL_EXIT_TO_XEN + /* + * Requires %rbx=stack_end + * Clobbers %rax, %rcx, %rdx +-- +2.41.0 + diff --git a/xsa439-0003-x86-spec-ctrl-Fold-DO_SPEC_CTRL_EXIT_TO_XEN-into-it-.patch b/xsa439-0003-x86-spec-ctrl-Fold-DO_SPEC_CTRL_EXIT_TO_XEN-into-it-.patch new file mode 100644 index 0000000..f3d6120 --- /dev/null +++ b/xsa439-0003-x86-spec-ctrl-Fold-DO_SPEC_CTRL_EXIT_TO_XEN-into-it-.patch @@ -0,0 +1,88 @@ +From afa5b17f385372226de6b0862f12ab39fda16b5c Mon Sep 17 00:00:00 2001 +Message-ID: +In-Reply-To: <08539e8315fdae5f5bfd655d53ed35fd2922fe6c.1695742580.git.m.a.young@durham.ac.uk> +References: <08539e8315fdae5f5bfd655d53ed35fd2922fe6c.1695742580.git.m.a.young@durham.ac.uk> +From: Andrew Cooper +Date: Tue, 12 Sep 2023 17:03:16 +0100 +Subject: [XEN PATCH 03/10] x86/spec-ctrl: Fold DO_SPEC_CTRL_EXIT_TO_XEN into + it's single user + +With the SPEC_CTRL_EXIT_TO_XEN{,_IST} confusion fixed, it's now obvious that +there's only a single EXIT_TO_XEN path. Fold DO_SPEC_CTRL_EXIT_TO_XEN into +SPEC_CTRL_EXIT_TO_XEN to simplify further fixes. + +When merging labels, switch the name to .L\@_skip_sc_msr as "skip" on its own +is going to be too generic shortly. + +No functional change. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 694bb0f280fd08a4377e36e32b84b5062def4de2) +--- + xen/include/asm-x86/spec_ctrl_asm.h | 40 ++++++++++++----------------- + 1 file changed, 16 insertions(+), 24 deletions(-) + +diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h +index f5110616e4..251c30eee5 100644 +--- a/xen/include/asm-x86/spec_ctrl_asm.h ++++ b/xen/include/asm-x86/spec_ctrl_asm.h +@@ -216,27 +216,6 @@ + wrmsr + .endm + +-.macro DO_SPEC_CTRL_EXIT_TO_XEN +-/* +- * Requires %rbx=stack_end +- * Clobbers %rax, %rcx, %rdx +- * +- * When returning to Xen context, look to see whether SPEC_CTRL shadowing is +- * in effect, and reload the shadow value. This covers race conditions which +- * exist with an NMI/MCE/etc hitting late in the return-to-guest path. +- */ +- xor %edx, %edx +- +- testb $SCF_use_shadow, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx) +- jz .L\@_skip +- +- mov STACK_CPUINFO_FIELD(shadow_spec_ctrl)(%rbx), %eax +- mov $MSR_SPEC_CTRL, %ecx +- wrmsr +- +-.L\@_skip: +-.endm +- + .macro DO_SPEC_CTRL_EXIT_TO_GUEST + /* + * Requires %eax=spec_ctrl, %rsp=regs/cpuinfo +@@ -345,11 +324,24 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + * Clobbers %rax, %rcx, %rdx + */ + testb $SCF_ist_sc_msr, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx) +- jz .L\@_skip ++ jz .L\@_skip_sc_msr + +- DO_SPEC_CTRL_EXIT_TO_XEN ++ /* ++ * When returning to Xen context, look to see whether SPEC_CTRL shadowing ++ * is in effect, and reload the shadow value. This covers race conditions ++ * which exist with an NMI/MCE/etc hitting late in the return-to-guest ++ * path. ++ */ ++ xor %edx, %edx + +-.L\@_skip: ++ testb $SCF_use_shadow, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx) ++ jz .L\@_skip_sc_msr ++ ++ mov STACK_CPUINFO_FIELD(shadow_spec_ctrl)(%rbx), %eax ++ mov $MSR_SPEC_CTRL, %ecx ++ wrmsr ++ ++.L\@_skip_sc_msr: + .endm + + #endif /* __ASSEMBLY__ */ +-- +2.41.0 + diff --git a/xsa439-0004-x86-spec-ctrl-Turn-the-remaining-SPEC_CTRL_-ENTRY-EX.patch b/xsa439-0004-x86-spec-ctrl-Turn-the-remaining-SPEC_CTRL_-ENTRY-EX.patch new file mode 100644 index 0000000..8f44144 --- /dev/null +++ b/xsa439-0004-x86-spec-ctrl-Turn-the-remaining-SPEC_CTRL_-ENTRY-EX.patch @@ -0,0 +1,86 @@ +From 353e876a9dd5a93d0bf8819e77613c33db0de97b Mon Sep 17 00:00:00 2001 +Message-ID: <353e876a9dd5a93d0bf8819e77613c33db0de97b.1695742580.git.m.a.young@durham.ac.uk> +In-Reply-To: <08539e8315fdae5f5bfd655d53ed35fd2922fe6c.1695742580.git.m.a.young@durham.ac.uk> +References: <08539e8315fdae5f5bfd655d53ed35fd2922fe6c.1695742580.git.m.a.young@durham.ac.uk> +From: Andrew Cooper +Date: Fri, 1 Sep 2023 11:38:44 +0100 +Subject: [XEN PATCH 04/10] x86/spec-ctrl: Turn the remaining + SPEC_CTRL_{ENTRY,EXIT}_* into asm macros + +These have grown more complex over time, with some already having been +converted. + +Provide full Requires/Clobbers comments, otherwise missing at this level of +indirection. + +No functional change. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 7125429aafb9e3c9c88fc93001fc2300e0ac2cc8) +--- + xen/include/asm-x86/spec_ctrl_asm.h | 37 ++++++++++++++++++++++------- + 1 file changed, 28 insertions(+), 9 deletions(-) + +diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h +index 251c30eee5..94ed5dc880 100644 +--- a/xen/include/asm-x86/spec_ctrl_asm.h ++++ b/xen/include/asm-x86/spec_ctrl_asm.h +@@ -236,26 +236,45 @@ + .endm + + /* Use after an entry from PV context (syscall/sysenter/int80/int82/etc). */ +-#define SPEC_CTRL_ENTRY_FROM_PV \ ++.macro SPEC_CTRL_ENTRY_FROM_PV ++/* ++ * Requires %rsp=regs/cpuinfo, %rdx=0 ++ * Clobbers %rax, %rcx, %rdx ++ */ + ALTERNATIVE "", __stringify(DO_SPEC_CTRL_COND_IBPB maybexen=0), \ +- X86_FEATURE_IBPB_ENTRY_PV; \ +- ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV; \ ++ X86_FEATURE_IBPB_ENTRY_PV ++ ++ ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV ++ + ALTERNATIVE "", __stringify(DO_SPEC_CTRL_ENTRY maybexen=0), \ + X86_FEATURE_SC_MSR_PV ++.endm + + /* Use in interrupt/exception context. May interrupt Xen or PV context. */ +-#define SPEC_CTRL_ENTRY_FROM_INTR \ ++.macro SPEC_CTRL_ENTRY_FROM_INTR ++/* ++ * Requires %rsp=regs, %r14=stack_end, %rdx=0 ++ * Clobbers %rax, %rcx, %rdx ++ */ + ALTERNATIVE "", __stringify(DO_SPEC_CTRL_COND_IBPB maybexen=1), \ +- X86_FEATURE_IBPB_ENTRY_PV; \ +- ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV; \ ++ X86_FEATURE_IBPB_ENTRY_PV ++ ++ ALTERNATIVE "", DO_OVERWRITE_RSB, X86_FEATURE_SC_RSB_PV ++ + ALTERNATIVE "", __stringify(DO_SPEC_CTRL_ENTRY maybexen=1), \ + X86_FEATURE_SC_MSR_PV ++.endm + + /* Use when exiting to PV guest context. */ +-#define SPEC_CTRL_EXIT_TO_PV \ +- ALTERNATIVE "", \ +- DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_PV; \ ++.macro SPEC_CTRL_EXIT_TO_PV ++/* ++ * Requires %rax=spec_ctrl, %rsp=regs/info ++ * Clobbers %rcx, %rdx ++ */ ++ ALTERNATIVE "", DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_PV ++ + DO_SPEC_CTRL_COND_VERW ++.endm + + /* + * Use in IST interrupt/exception context. May interrupt Xen or PV context. +-- +2.41.0 + diff --git a/xsa439-0005-x86-spec-ctrl-Improve-all-SPEC_CTRL_-ENTER-EXIT-_-co.patch b/xsa439-0005-x86-spec-ctrl-Improve-all-SPEC_CTRL_-ENTER-EXIT-_-co.patch new file mode 100644 index 0000000..4571046 --- /dev/null +++ b/xsa439-0005-x86-spec-ctrl-Improve-all-SPEC_CTRL_-ENTER-EXIT-_-co.patch @@ -0,0 +1,109 @@ +From 6cc49c355e952f4ff564c6b817e7eff57c5a02c7 Mon Sep 17 00:00:00 2001 +Message-ID: <6cc49c355e952f4ff564c6b817e7eff57c5a02c7.1695742580.git.m.a.young@durham.ac.uk> +In-Reply-To: <08539e8315fdae5f5bfd655d53ed35fd2922fe6c.1695742580.git.m.a.young@durham.ac.uk> +References: <08539e8315fdae5f5bfd655d53ed35fd2922fe6c.1695742580.git.m.a.young@durham.ac.uk> +From: Andrew Cooper +Date: Wed, 30 Aug 2023 20:11:50 +0100 +Subject: [XEN PATCH 05/10] x86/spec-ctrl: Improve all SPEC_CTRL_{ENTER,EXIT}_* + comments + +... to better explain how they're used. + +Doing so highlights that SPEC_CTRL_EXIT_TO_XEN is missing a VERW flush for the +corner case when e.g. an NMI hits late in an exit-to-guest path. + +Leave a TODO, which will be addressed in subsequent patches which arrange for +VERW flushing to be safe within SPEC_CTRL_EXIT_TO_XEN. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 45f00557350dc7d0756551069803fc49c29184ca) +--- + xen/include/asm-x86/spec_ctrl_asm.h | 36 +++++++++++++++++++++++++---- + 1 file changed, 31 insertions(+), 5 deletions(-) + +diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h +index 94ed5dc880..9c397f7cbd 100644 +--- a/xen/include/asm-x86/spec_ctrl_asm.h ++++ b/xen/include/asm-x86/spec_ctrl_asm.h +@@ -235,7 +235,10 @@ + wrmsr + .endm + +-/* Use after an entry from PV context (syscall/sysenter/int80/int82/etc). */ ++/* ++ * Used after an entry from PV context: SYSCALL, SYSENTER, INT, ++ * etc. There is always a guest speculation state in context. ++ */ + .macro SPEC_CTRL_ENTRY_FROM_PV + /* + * Requires %rsp=regs/cpuinfo, %rdx=0 +@@ -250,7 +253,11 @@ + X86_FEATURE_SC_MSR_PV + .endm + +-/* Use in interrupt/exception context. May interrupt Xen or PV context. */ ++/* ++ * Used after an exception or maskable interrupt, hitting Xen or PV context. ++ * There will either be a guest speculation context, or (barring fatal ++ * exceptions) a well-formed Xen speculation context. ++ */ + .macro SPEC_CTRL_ENTRY_FROM_INTR + /* + * Requires %rsp=regs, %r14=stack_end, %rdx=0 +@@ -265,7 +272,10 @@ + X86_FEATURE_SC_MSR_PV + .endm + +-/* Use when exiting to PV guest context. */ ++/* ++ * Used when exiting from any entry context, back to PV context. This ++ * includes from an IST entry which moved onto the primary stack. ++ */ + .macro SPEC_CTRL_EXIT_TO_PV + /* + * Requires %rax=spec_ctrl, %rsp=regs/info +@@ -277,7 +287,13 @@ + .endm + + /* +- * Use in IST interrupt/exception context. May interrupt Xen or PV context. ++ * Used after an IST entry hitting Xen or PV context. Special care is needed, ++ * because when hitting Xen context, there may not be a well-formed ++ * speculation context. (i.e. it can hit in the middle of ++ * SPEC_CTRL_{ENTRY,EXIT}_* regions.) ++ * ++ * An IST entry which hits PV context moves onto the primary stack and leaves ++ * via SPEC_CTRL_EXIT_TO_PV, *not* SPEC_CTRL_EXIT_TO_XEN. + */ + .macro SPEC_CTRL_ENTRY_FROM_INTR_IST + /* +@@ -336,7 +352,14 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + UNLIKELY_END(\@_serialise) + .endm + +-/* Use when exiting to Xen context. */ ++/* ++ * Use when exiting from any entry context, back to Xen context. This ++ * includes returning to other SPEC_CTRL_{ENTRY,EXIT}_* regions with an ++ * incomplete speculation context. ++ * ++ * Because we might have interrupted Xen beyond SPEC_CTRL_EXIT_TO_$GUEST, we ++ * need to treat this as if it were an EXIT_TO_$GUEST case too. ++ */ + .macro SPEC_CTRL_EXIT_TO_XEN + /* + * Requires %rbx=stack_end +@@ -361,6 +384,9 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + wrmsr + + .L\@_skip_sc_msr: ++ ++ /* TODO VERW */ ++ + .endm + + #endif /* __ASSEMBLY__ */ +-- +2.41.0 + diff --git a/xsa439-0006-x86-entry-Adjust-restore_all_xen-to-hold-stack_end-i.patch b/xsa439-0006-x86-entry-Adjust-restore_all_xen-to-hold-stack_end-i.patch new file mode 100644 index 0000000..08b96f9 --- /dev/null +++ b/xsa439-0006-x86-entry-Adjust-restore_all_xen-to-hold-stack_end-i.patch @@ -0,0 +1,77 @@ +From 19aca8f32778f289112fc8db2ee547cdf29c81ca Mon Sep 17 00:00:00 2001 +Message-ID: <19aca8f32778f289112fc8db2ee547cdf29c81ca.1695742580.git.m.a.young@durham.ac.uk> +In-Reply-To: <08539e8315fdae5f5bfd655d53ed35fd2922fe6c.1695742580.git.m.a.young@durham.ac.uk> +References: <08539e8315fdae5f5bfd655d53ed35fd2922fe6c.1695742580.git.m.a.young@durham.ac.uk> +From: Andrew Cooper +Date: Wed, 13 Sep 2023 13:48:16 +0100 +Subject: [XEN PATCH 06/10] x86/entry: Adjust restore_all_xen to hold stack_end + in %r14 + +All other SPEC_CTRL_{ENTRY,EXIT}_* helpers hold stack_end in %r14. Adjust it +for consistency. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 7aa28849a1155d856e214e9a80a7e65fffdc3e58) +--- + xen/arch/x86/x86_64/entry.S | 8 ++++---- + xen/include/asm-x86/spec_ctrl_asm.h | 8 ++++---- + 2 files changed, 8 insertions(+), 8 deletions(-) + +diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S +index 59f2040787..266c0a0990 100644 +--- a/xen/arch/x86/x86_64/entry.S ++++ b/xen/arch/x86/x86_64/entry.S +@@ -656,15 +656,15 @@ restore_all_xen: + * Check whether we need to switch to the per-CPU page tables, in + * case we return to late PV exit code (from an NMI or #MC). + */ +- GET_STACK_END(bx) +- cmpb $0, STACK_CPUINFO_FIELD(use_pv_cr3)(%rbx) ++ GET_STACK_END(14) ++ cmpb $0, STACK_CPUINFO_FIELD(use_pv_cr3)(%r14) + UNLIKELY_START(ne, exit_cr3) +- mov STACK_CPUINFO_FIELD(pv_cr3)(%rbx), %rax ++ mov STACK_CPUINFO_FIELD(pv_cr3)(%r14), %rax + mov %rax, %cr3 + UNLIKELY_END(exit_cr3) + + /* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */ +- SPEC_CTRL_EXIT_TO_XEN /* Req: %rbx=end, Clob: acd */ ++ SPEC_CTRL_EXIT_TO_XEN /* Req: %r14=end, Clob: acd */ + + RESTORE_ALL adj=8 + iretq +diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h +index 9c397f7cbd..3e745813cf 100644 +--- a/xen/include/asm-x86/spec_ctrl_asm.h ++++ b/xen/include/asm-x86/spec_ctrl_asm.h +@@ -362,10 +362,10 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + */ + .macro SPEC_CTRL_EXIT_TO_XEN + /* +- * Requires %rbx=stack_end ++ * Requires %r14=stack_end + * Clobbers %rax, %rcx, %rdx + */ +- testb $SCF_ist_sc_msr, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx) ++ testb $SCF_ist_sc_msr, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14) + jz .L\@_skip_sc_msr + + /* +@@ -376,10 +376,10 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + */ + xor %edx, %edx + +- testb $SCF_use_shadow, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%rbx) ++ testb $SCF_use_shadow, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14) + jz .L\@_skip_sc_msr + +- mov STACK_CPUINFO_FIELD(shadow_spec_ctrl)(%rbx), %eax ++ mov STACK_CPUINFO_FIELD(shadow_spec_ctrl)(%r14), %eax + mov $MSR_SPEC_CTRL, %ecx + wrmsr + +-- +2.41.0 + diff --git a/xsa439-0007-x86-entry-Track-the-IST-ness-of-an-entry-for-the-exi.patch b/xsa439-0007-x86-entry-Track-the-IST-ness-of-an-entry-for-the-exi.patch new file mode 100644 index 0000000..4cd0838 --- /dev/null +++ b/xsa439-0007-x86-entry-Track-the-IST-ness-of-an-entry-for-the-exi.patch @@ -0,0 +1,112 @@ +From 8064cbdbef79e328fad5158beeaf1c45bd0f5bd3 Mon Sep 17 00:00:00 2001 +Message-ID: <8064cbdbef79e328fad5158beeaf1c45bd0f5bd3.1695742580.git.m.a.young@durham.ac.uk> +In-Reply-To: <08539e8315fdae5f5bfd655d53ed35fd2922fe6c.1695742580.git.m.a.young@durham.ac.uk> +References: <08539e8315fdae5f5bfd655d53ed35fd2922fe6c.1695742580.git.m.a.young@durham.ac.uk> +From: Andrew Cooper +Date: Wed, 13 Sep 2023 12:20:12 +0100 +Subject: [XEN PATCH 07/10] x86/entry: Track the IST-ness of an entry for the + exit paths + +Use %r12 to hold an ist_exit boolean. This register is zero elsewhere in the +entry/exit asm, so it only needs setting in the IST path. + +As this is subtle and fragile, add check_ist_exit() to be used in debugging +builds to cross-check that the ist_exit boolean matches the entry vector. + +Write check_ist_exit() it in C, because it's debug only and the logic more +complicated than I care to maintain in asm. + +For now, we only need to use this signal in the exit-to-Xen path, but some +exit-to-guest paths happen in IST context too. Check the correctness in all +exit paths to avoid the logic bit-rotting. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 21bdc25b05a0f8ab6bc73520a9ca01327360732c) + +x86/entry: Partially revert IST-exit checks + +The patch adding check_ist_exit() didn't account for the fact that +reset_stack_and_jump() is not an ABI-preserving boundary. The IST-ness in +%r12 doesn't survive into the next context, and is a stale value C. + +This shows up in Gitlab CI for the Clang build: + + https://gitlab.com/xen-project/people/andyhhp/xen/-/jobs/5112783827 + +and in OSSTest for GCC 8: + + http://logs.test-lab.xenproject.org/osstest/logs/183045/test-amd64-amd64-xl-qemuu-debianhvm-amd64/serial-pinot0.log + +There's no straightforward way to reconstruct the IST-exit-ness on the +exit-to-guest path after a context switch. For now, we only need IST-exit on +the return-to-Xen path. + +Fixes: 21bdc25b05a0 ("x86/entry: Track the IST-ness of an entry for the exit paths") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 9b57c800b79b96769ea3dcd6468578fa664d19f9) +--- + xen/arch/x86/traps.c | 13 +++++++++++++ + xen/arch/x86/x86_64/entry.S | 13 ++++++++++++- + 2 files changed, 25 insertions(+), 1 deletion(-) + +diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c +index 9679bfdb08..f7992ff230 100644 +--- a/xen/arch/x86/traps.c ++++ b/xen/arch/x86/traps.c +@@ -2348,6 +2348,19 @@ void asm_domain_crash_synchronous(unsigned long addr) + do_softirq(); + } + ++#ifdef CONFIG_DEBUG ++void check_ist_exit(const struct cpu_user_regs *regs, bool ist_exit) ++{ ++ const unsigned int ist_mask = ++ (1U << X86_EXC_NMI) | (1U << X86_EXC_DB) | ++ (1U << X86_EXC_DF) | (1U << X86_EXC_MC); ++ uint8_t ev = regs->entry_vector; ++ bool is_ist = (ev < TRAP_nr) && ((1U << ev) & ist_mask); ++ ++ ASSERT(is_ist == ist_exit); ++} ++#endif ++ + /* + * Local variables: + * mode: C +diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S +index 266c0a0990..671e3b3fd5 100644 +--- a/xen/arch/x86/x86_64/entry.S ++++ b/xen/arch/x86/x86_64/entry.S +@@ -650,8 +650,15 @@ ret_from_intr: + .section .text.entry, "ax", @progbits + + ALIGN +-/* No special register assumptions. */ ++/* %r12=ist_exit */ + restore_all_xen: ++ ++#ifdef CONFIG_DEBUG ++ mov %rsp, %rdi ++ mov %r12, %rsi ++ call check_ist_exit ++#endif ++ + /* + * Check whether we need to switch to the per-CPU page tables, in + * case we return to late PV exit code (from an NMI or #MC). +@@ -1032,6 +1039,10 @@ handle_ist_exception: + INDIRECT_CALL %rdx + mov %r15, STACK_CPUINFO_FIELD(xen_cr3)(%r14) + mov %bl, STACK_CPUINFO_FIELD(use_pv_cr3)(%r14) ++ ++ /* This is an IST exit */ ++ mov $1, %r12d ++ + cmpb $TRAP_nmi,UREGS_entry_vector(%rsp) + jne ret_from_intr + +-- +2.41.0 + diff --git a/xsa439-0008-x86-spec-ctrl-Issue-VERW-during-IST-exit-to-Xen.patch b/xsa439-0008-x86-spec-ctrl-Issue-VERW-during-IST-exit-to-Xen.patch new file mode 100644 index 0000000..1c8cdb0 --- /dev/null +++ b/xsa439-0008-x86-spec-ctrl-Issue-VERW-during-IST-exit-to-Xen.patch @@ -0,0 +1,92 @@ +From 3e51782ebb088fde39fdcfa30d002baddd1a9e06 Mon Sep 17 00:00:00 2001 +Message-ID: <3e51782ebb088fde39fdcfa30d002baddd1a9e06.1695742580.git.m.a.young@durham.ac.uk> +In-Reply-To: <08539e8315fdae5f5bfd655d53ed35fd2922fe6c.1695742580.git.m.a.young@durham.ac.uk> +References: <08539e8315fdae5f5bfd655d53ed35fd2922fe6c.1695742580.git.m.a.young@durham.ac.uk> +From: Andrew Cooper +Date: Wed, 13 Sep 2023 13:53:33 +0100 +Subject: [XEN PATCH 08/10] x86/spec-ctrl: Issue VERW during IST exit to Xen + +There is a corner case where e.g. an NMI hitting an exit-to-guest path after +SPEC_CTRL_EXIT_TO_* would have run the entire NMI handler *after* the VERW +flush to scrub potentially sensitive data from uarch buffers. + +In order to compensate, issue VERW when exiting to Xen from an IST entry. + +SPEC_CTRL_EXIT_TO_XEN already has two reads of spec_ctrl_flags off the stack, +and we're about to add a third. Load the field into %ebx, and list the +register as clobbered. + +%r12 has been arranged to be the ist_exit signal, so add this as an input +dependency and use it to identify when to issue a VERW. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit 3ee6066bcd737756b0990d417d94eddc0b0d2585) +--- + xen/arch/x86/x86_64/entry.S | 2 +- + xen/include/asm-x86/spec_ctrl_asm.h | 20 +++++++++++++++----- + 2 files changed, 16 insertions(+), 6 deletions(-) + +diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S +index 671e3b3fd5..88ff5c150f 100644 +--- a/xen/arch/x86/x86_64/entry.S ++++ b/xen/arch/x86/x86_64/entry.S +@@ -671,7 +671,7 @@ UNLIKELY_START(ne, exit_cr3) + UNLIKELY_END(exit_cr3) + + /* WARNING! `ret`, `call *`, `jmp *` not safe beyond this point. */ +- SPEC_CTRL_EXIT_TO_XEN /* Req: %r14=end, Clob: acd */ ++ SPEC_CTRL_EXIT_TO_XEN /* Req: %r12=ist_exit %r14=end, Clob: abcd */ + + RESTORE_ALL adj=8 + iretq +diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h +index 3e745813cf..8a816b8cf6 100644 +--- a/xen/include/asm-x86/spec_ctrl_asm.h ++++ b/xen/include/asm-x86/spec_ctrl_asm.h +@@ -362,10 +362,12 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + */ + .macro SPEC_CTRL_EXIT_TO_XEN + /* +- * Requires %r14=stack_end +- * Clobbers %rax, %rcx, %rdx ++ * Requires %r12=ist_exit, %r14=stack_end ++ * Clobbers %rax, %rbx, %rcx, %rdx + */ +- testb $SCF_ist_sc_msr, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14) ++ movzbl STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14), %ebx ++ ++ testb $SCF_ist_sc_msr, %bl + jz .L\@_skip_sc_msr + + /* +@@ -376,7 +378,7 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + */ + xor %edx, %edx + +- testb $SCF_use_shadow, STACK_CPUINFO_FIELD(spec_ctrl_flags)(%r14) ++ testb $SCF_use_shadow, %bl + jz .L\@_skip_sc_msr + + mov STACK_CPUINFO_FIELD(shadow_spec_ctrl)(%r14), %eax +@@ -385,8 +387,16 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + + .L\@_skip_sc_msr: + +- /* TODO VERW */ ++ test %r12, %r12 ++ jz .L\@_skip_ist_exit ++ ++ /* Logically DO_SPEC_CTRL_COND_VERW but without the %rsp=cpuinfo dependency */ ++ testb $SCF_verw, %bl ++ jz .L\@_skip_verw ++ verw STACK_CPUINFO_FIELD(verw_sel)(%r14) ++.L\@_skip_verw: + ++.L\@_skip_ist_exit: + .endm + + #endif /* __ASSEMBLY__ */ +-- +2.41.0 + diff --git a/xsa439-0009-x86-amd-Introduce-is_zen-1-2-_uarch-predicates.patch b/xsa439-0009-x86-amd-Introduce-is_zen-1-2-_uarch-predicates.patch new file mode 100644 index 0000000..59075ce --- /dev/null +++ b/xsa439-0009-x86-amd-Introduce-is_zen-1-2-_uarch-predicates.patch @@ -0,0 +1,94 @@ +From a5857f1eca17a609119ae928c9fa73bb0996ddd9 Mon Sep 17 00:00:00 2001 +Message-ID: +In-Reply-To: <08539e8315fdae5f5bfd655d53ed35fd2922fe6c.1695742580.git.m.a.young@durham.ac.uk> +References: <08539e8315fdae5f5bfd655d53ed35fd2922fe6c.1695742580.git.m.a.young@durham.ac.uk> +From: Andrew Cooper +Date: Fri, 15 Sep 2023 12:13:51 +0100 +Subject: [XEN PATCH 09/10] x86/amd: Introduce is_zen{1,2}_uarch() predicates + +We already have 3 cases using STIBP as a Zen1/2 heuristic, and are about to +introduce a 4th. Wrap the heuristic into a pair of predicates rather than +opencoding it, and the explanation of the heuristic, at each usage site. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit de1d265001397f308c5c3c5d3ffc30e7ef8c0705) +--- + xen/arch/x86/cpu/amd.c | 18 ++++-------------- + xen/include/asm-x86/amd.h | 11 +++++++++++ + 2 files changed, 15 insertions(+), 14 deletions(-) + +diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c +index a591038757..b71d891901 100644 +--- a/xen/arch/x86/cpu/amd.c ++++ b/xen/arch/x86/cpu/amd.c +@@ -746,15 +746,13 @@ void amd_init_ssbd(const struct cpuinfo_x86 *c) + * non-branch instructions to be ignored. It is to be set unilaterally in + * newer microcode. + * +- * This chickenbit is something unrelated on Zen1, and Zen1 vs Zen2 isn't a +- * simple model number comparison, so use STIBP as a heuristic to separate the +- * two uarches in Fam17h(AMD)/18h(Hygon). ++ * This chickenbit is something unrelated on Zen1. + */ + void amd_init_spectral_chicken(void) + { + uint64_t val, chickenbit = 1 << 1; + +- if (cpu_has_hypervisor || !boot_cpu_has(X86_FEATURE_AMD_STIBP)) ++ if (cpu_has_hypervisor || !is_zen2_uarch()) + return; + + if (rdmsr_safe(MSR_AMD64_DE_CFG2, val) == 0 && !(val & chickenbit)) +@@ -803,11 +801,8 @@ void amd_check_zenbleed(void) + * With the Fam17h check above, most parts getting here are + * Zen1. They're not affected. Assume Zen2 ones making it + * here are affected regardless of microcode version. +- * +- * Zen1 vs Zen2 isn't a simple model number comparison, so use +- * STIBP as a heuristic to distinguish. + */ +- if (!boot_cpu_has(X86_FEATURE_AMD_STIBP)) ++ if (is_zen1_uarch()) + return; + good_rev = ~0U; + break; +@@ -1168,12 +1163,7 @@ static int __init zen2_c6_errata_check(void) + */ + s_time_t delta; + +- /* +- * Zen1 vs Zen2 isn't a simple model number comparison, so use STIBP as +- * a heuristic to separate the two uarches in Fam17h. +- */ +- if (cpu_has_hypervisor || boot_cpu_data.x86 != 0x17 || +- !boot_cpu_has(X86_FEATURE_AMD_STIBP)) ++ if (cpu_has_hypervisor || boot_cpu_data.x86 != 0x17 || !is_zen2_uarch()) + return 0; + + /* +diff --git a/xen/include/asm-x86/amd.h b/xen/include/asm-x86/amd.h +index a82382e6bf..7fe1e19217 100644 +--- a/xen/include/asm-x86/amd.h ++++ b/xen/include/asm-x86/amd.h +@@ -140,6 +140,17 @@ + AMD_MODEL_RANGE(0x11, 0x0, 0x0, 0xff, 0xf), \ + AMD_MODEL_RANGE(0x12, 0x0, 0x0, 0xff, 0xf)) + ++/* ++ * The Zen1 and Zen2 microarchitectures are implemented by AMD (Fam17h) and ++ * Hygon (Fam18h) but without simple model number rules. Instead, use STIBP ++ * as a heuristic that distinguishes the two. ++ * ++ * The caller is required to perform the appropriate vendor/family checks ++ * first. ++ */ ++#define is_zen1_uarch() (!boot_cpu_has(X86_FEATURE_AMD_STIBP)) ++#define is_zen2_uarch() boot_cpu_has(X86_FEATURE_AMD_STIBP) ++ + struct cpuinfo_x86; + int cpu_has_amd_erratum(const struct cpuinfo_x86 *, int, ...); + +-- +2.41.0 + diff --git a/xsa439-0010-x86-spec-ctrl-Mitigate-the-Zen1-DIV-leakage.patch b/xsa439-0010-x86-spec-ctrl-Mitigate-the-Zen1-DIV-leakage.patch new file mode 100644 index 0000000..0b063fb --- /dev/null +++ b/xsa439-0010-x86-spec-ctrl-Mitigate-the-Zen1-DIV-leakage.patch @@ -0,0 +1,239 @@ +From de751c3d906d17b2e25ee429f81b17a689c7c6c0 Mon Sep 17 00:00:00 2001 +Message-ID: +In-Reply-To: <08539e8315fdae5f5bfd655d53ed35fd2922fe6c.1695742580.git.m.a.young@durham.ac.uk> +References: <08539e8315fdae5f5bfd655d53ed35fd2922fe6c.1695742580.git.m.a.young@durham.ac.uk> +From: Andrew Cooper +Date: Wed, 30 Aug 2023 20:24:25 +0100 +Subject: [XEN PATCH 10/10] x86/spec-ctrl: Mitigate the Zen1 DIV leakage + +In the Zen1 microarchitecure, there is one divider in the pipeline which +services uops from both threads. In the case of #DE, the latched result from +the previous DIV to execute will be forwarded speculatively. + +This is an interesting covert channel that allows two threads to communicate +without any system calls. In also allows userspace to obtain the result of +the most recent DIV instruction executed (even speculatively) in the core, +which can be from a higher privilege context. + +Scrub the result from the divider by executing a non-faulting divide. This +needs performing on the exit-to-guest paths, and ist_exit-to-Xen. + +Alternatives in IST context is believed safe now that it's done in NMI +context. + +This is XSA-439 / CVE-2023-20588. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +(cherry picked from commit b5926c6ecf05c28ee99c6248c42d691ccbf0c315) +--- + docs/misc/xen-command-line.pandoc | 6 +++- + xen/arch/x86/hvm/svm/entry.S | 1 + + xen/arch/x86/spec_ctrl.c | 49 ++++++++++++++++++++++++++++- + xen/include/asm-x86/cpufeatures.h | 2 +- + xen/include/asm-x86/spec_ctrl_asm.h | 17 ++++++++++ + 5 files changed, 72 insertions(+), 3 deletions(-) + +diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc +index a37a3890d1..a7a1362bac 100644 +--- a/docs/misc/xen-command-line.pandoc ++++ b/docs/misc/xen-command-line.pandoc +@@ -2263,7 +2263,7 @@ By default SSBD will be mitigated at runtime (i.e `ssbd=runtime`). + > {msr-sc,rsb,md-clear,ibpb-entry}=|{pv,hvm}=, + > bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,psfd, + > eager-fpu,l1d-flush,branch-harden,srb-lock, +-> unpriv-mmio,gds-mit}= ]` ++> unpriv-mmio,gds-mit,div-scrub}= ]` + + Controls for speculative execution sidechannel mitigations. By default, Xen + will pick the most appropriate mitigations based on compiled in support, +@@ -2383,6 +2383,10 @@ has elected not to lock the configuration, Xen will use GDS_CTRL to mitigate + GDS with. Otherwise, Xen will mitigate by disabling AVX, which blocks the use + of the AVX2 Gather instructions. + ++On all hardware, the `div-scrub=` option can be used to force or prevent Xen ++from mitigating the DIV-leakage vulnerability. By default, Xen will mitigate ++DIV-leakage on hardware believed to be vulnerable. ++ + ### sync_console + > `= ` + +diff --git a/xen/arch/x86/hvm/svm/entry.S b/xen/arch/x86/hvm/svm/entry.S +index 0ff4008060..ad5ca50c12 100644 +--- a/xen/arch/x86/hvm/svm/entry.S ++++ b/xen/arch/x86/hvm/svm/entry.S +@@ -72,6 +72,7 @@ __UNLIKELY_END(nsvm_hap) + 1: /* No Spectre v1 concerns. Execution will hit VMRUN imminently. */ + .endm + ALTERNATIVE "", svm_vmentry_spec_ctrl, X86_FEATURE_SC_MSR_HVM ++ ALTERNATIVE "", DO_SPEC_CTRL_DIV, X86_FEATURE_SC_DIV + + pop %r15 + pop %r14 +diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c +index 082445179d..6e82a126a3 100644 +--- a/xen/arch/x86/spec_ctrl.c ++++ b/xen/arch/x86/spec_ctrl.c +@@ -22,6 +22,7 @@ + #include + #include + ++#include + #include + #include + #include +@@ -78,6 +79,7 @@ static int8_t __initdata opt_srb_lock = -1; + static bool __initdata opt_unpriv_mmio; + static bool __read_mostly opt_fb_clear_mmio; + static int8_t __initdata opt_gds_mit = -1; ++static int8_t __initdata opt_div_scrub = -1; + + static int __init parse_spec_ctrl(const char *s) + { +@@ -132,6 +134,7 @@ static int __init parse_spec_ctrl(const char *s) + opt_srb_lock = 0; + opt_unpriv_mmio = false; + opt_gds_mit = 0; ++ opt_div_scrub = 0; + } + else if ( val > 0 ) + rc = -EINVAL; +@@ -284,6 +287,8 @@ static int __init parse_spec_ctrl(const char *s) + opt_unpriv_mmio = val; + else if ( (val = parse_boolean("gds-mit", s, ss)) >= 0 ) + opt_gds_mit = val; ++ else if ( (val = parse_boolean("div-scrub", s, ss)) >= 0 ) ++ opt_div_scrub = val; + else + rc = -EINVAL; + +@@ -484,7 +489,7 @@ static void __init print_details(enum ind_thunk thunk) + "\n"); + + /* Settings for Xen's protection, irrespective of guests. */ +- printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s%s%s, Other:%s%s%s%s%s\n", ++ printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s%s%s, Other:%s%s%s%s%s%s\n", + thunk == THUNK_NONE ? "N/A" : + thunk == THUNK_RETPOLINE ? "RETPOLINE" : + thunk == THUNK_LFENCE ? "LFENCE" : +@@ -509,6 +514,7 @@ static void __init print_details(enum ind_thunk thunk) + opt_l1d_flush ? " L1D_FLUSH" : "", + opt_md_clear_pv || opt_md_clear_hvm || + opt_fb_clear_mmio ? " VERW" : "", ++ opt_div_scrub ? " DIV" : "", + opt_branch_harden ? " BRANCH_HARDEN" : ""); + + /* L1TF diagnostics, printed if vulnerable or PV shadowing is in use. */ +@@ -962,6 +968,45 @@ static void __init srso_calculations(bool hw_smt_enabled) + setup_force_cpu_cap(X86_FEATURE_SRSO_NO); + } + ++/* ++ * The Div leakage issue is specific to the AMD Zen1 microarchitecure. ++ * ++ * However, there's no $FOO_NO bit defined, so if we're virtualised we have no ++ * hope of spotting the case where we might move to vulnerable hardware. We ++ * also can't make any useful conclusion about SMT-ness. ++ * ++ * Don't check the hypervisor bit, so at least we do the safe thing when ++ * booting on something that looks like a Zen1 CPU. ++ */ ++static bool __init has_div_vuln(void) ++{ ++ if ( !(boot_cpu_data.x86_vendor & ++ (X86_VENDOR_AMD | X86_VENDOR_HYGON)) ) ++ return false; ++ ++ if ( boot_cpu_data.x86 != 0x17 && boot_cpu_data.x86 != 0x18 ) ++ return false; ++ ++ return is_zen1_uarch(); ++} ++ ++static void __init div_calculations(bool hw_smt_enabled) ++{ ++ bool cpu_bug_div = has_div_vuln(); ++ ++ if ( opt_div_scrub == -1 ) ++ opt_div_scrub = cpu_bug_div; ++ ++ if ( opt_div_scrub ) ++ setup_force_cpu_cap(X86_FEATURE_SC_DIV); ++ ++ if ( opt_smt == -1 && !cpu_has_hypervisor && cpu_bug_div && hw_smt_enabled ) ++ warning_add( ++ "Booted on leaky-DIV hardware with SMT/Hyperthreading\n" ++ "enabled. Please assess your configuration and choose an\n" ++ "explicit 'smt=' setting. See XSA-439.\n"); ++} ++ + static void __init ibpb_calculations(void) + { + bool def_ibpb_entry = false; +@@ -1716,6 +1761,8 @@ void __init init_speculation_mitigations(void) + + ibpb_calculations(); + ++ div_calculations(hw_smt_enabled); ++ + /* Check whether Eager FPU should be enabled by default. */ + if ( opt_eager_fpu == -1 ) + opt_eager_fpu = should_use_eager_fpu(); +diff --git a/xen/include/asm-x86/cpufeatures.h b/xen/include/asm-x86/cpufeatures.h +index ccf9d7287c..70b93b6b44 100644 +--- a/xen/include/asm-x86/cpufeatures.h ++++ b/xen/include/asm-x86/cpufeatures.h +@@ -35,7 +35,7 @@ XEN_CPUFEATURE(SC_RSB_HVM, X86_SYNTH(19)) /* RSB overwrite needed for HVM + XEN_CPUFEATURE(XEN_SELFSNOOP, X86_SYNTH(20)) /* SELFSNOOP gets used by Xen itself */ + XEN_CPUFEATURE(SC_MSR_IDLE, X86_SYNTH(21)) /* Clear MSR_SPEC_CTRL on idle */ + XEN_CPUFEATURE(XEN_LBR, X86_SYNTH(22)) /* Xen uses MSR_DEBUGCTL.LBR */ +-/* Bits 23 unused. */ ++XEN_CPUFEATURE(SC_DIV, X86_SYNTH(23)) /* DIV scrub needed */ + XEN_CPUFEATURE(SC_RSB_IDLE, X86_SYNTH(24)) /* RSB overwrite needed for idle. */ + XEN_CPUFEATURE(SC_VERW_IDLE, X86_SYNTH(25)) /* VERW used by Xen for idle */ + XEN_CPUFEATURE(XEN_SHSTK, X86_SYNTH(26)) /* Xen uses CET Shadow Stacks */ +diff --git a/xen/include/asm-x86/spec_ctrl_asm.h b/xen/include/asm-x86/spec_ctrl_asm.h +index 8a816b8cf6..0e69971f66 100644 +--- a/xen/include/asm-x86/spec_ctrl_asm.h ++++ b/xen/include/asm-x86/spec_ctrl_asm.h +@@ -182,6 +182,19 @@ + .L\@_verw_skip: + .endm + ++.macro DO_SPEC_CTRL_DIV ++/* ++ * Requires nothing ++ * Clobbers %rax ++ * ++ * Issue a DIV for its flushing side effect (Zen1 uarch specific). Any ++ * non-faulting DIV will do; a byte DIV has least latency, and doesn't clobber ++ * %rdx. ++ */ ++ mov $1, %eax ++ div %al ++.endm ++ + .macro DO_SPEC_CTRL_ENTRY maybexen:req + /* + * Requires %rsp=regs (also cpuinfo if !maybexen) +@@ -284,6 +297,8 @@ + ALTERNATIVE "", DO_SPEC_CTRL_EXIT_TO_GUEST, X86_FEATURE_SC_MSR_PV + + DO_SPEC_CTRL_COND_VERW ++ ++ ALTERNATIVE "", DO_SPEC_CTRL_DIV, X86_FEATURE_SC_DIV + .endm + + /* +@@ -396,6 +411,8 @@ UNLIKELY_DISPATCH_LABEL(\@_serialise): + verw STACK_CPUINFO_FIELD(verw_sel)(%r14) + .L\@_skip_verw: + ++ ALTERNATIVE "", DO_SPEC_CTRL_DIV, X86_FEATURE_SC_DIV ++ + .L\@_skip_ist_exit: + .endm + +-- +2.41.0 + From 8b84ab579c25078db4fb4dc765d0f6489223fb59 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 11 Oct 2023 19:47:00 +0100 Subject: [PATCH 11/12] 4 security issues xenstored: A transaction conflict can crash C Xenstored [XSA-440, CVE-2023-34323] x86/AMD: missing IOMMU TLB flushing [XSA-442, CVE-2023-34326] Multiple vulnerabilities in libfsimage disk handling [XSA-443, CVE-2023-34325] x86/AMD: Debug Mask handling [XSA-444, CVE-2023-34327, CVE-2023-34328] --- xen.spec | 41 +++++- xsa440-4.17.patch | 58 ++++++++ xsa442-4.17.patch | 185 ++++++++++++++++++++++++++ xsa443-4.16-01.patch | 70 ++++++++++ xsa443-4.16-02.patch | 32 +++++ xsa443-4.16-03.patch | 137 +++++++++++++++++++ xsa443-4.16-04.patch | 60 +++++++++ xsa443-4.16-05.patch | 59 +++++++++ xsa443-4.16-06.patch | 65 +++++++++ xsa443-4.16-07.patch | 105 +++++++++++++++ xsa443-4.16-08.patch | 126 ++++++++++++++++++ xsa443-4.16-09.patch | 307 +++++++++++++++++++++++++++++++++++++++++++ xsa443-4.16-10.patch | 250 +++++++++++++++++++++++++++++++++++ xsa443-4.16-11.patch | 157 ++++++++++++++++++++++ xsa444-4.16-1.patch | 93 +++++++++++++ xsa444-4.16-2.patch | 72 ++++++++++ 16 files changed, 1816 insertions(+), 1 deletion(-) create mode 100644 xsa440-4.17.patch create mode 100644 xsa442-4.17.patch create mode 100644 xsa443-4.16-01.patch create mode 100644 xsa443-4.16-02.patch create mode 100644 xsa443-4.16-03.patch create mode 100644 xsa443-4.16-04.patch create mode 100644 xsa443-4.16-05.patch create mode 100644 xsa443-4.16-06.patch create mode 100644 xsa443-4.16-07.patch create mode 100644 xsa443-4.16-08.patch create mode 100644 xsa443-4.16-09.patch create mode 100644 xsa443-4.16-10.patch create mode 100644 xsa443-4.16-11.patch create mode 100644 xsa444-4.16-1.patch create mode 100644 xsa444-4.16-2.patch diff --git a/xen.spec b/xen.spec index 7c731d8..ee3bae8 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.5 -Release: 2%{?dist} +Release: 3%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -123,6 +123,21 @@ Patch55: xsa439-0007-x86-entry-Track-the-IST-ness-of-an-entry-for-the-exi.patch Patch56: xsa439-0008-x86-spec-ctrl-Issue-VERW-during-IST-exit-to-Xen.patch Patch57: xsa439-0009-x86-amd-Introduce-is_zen-1-2-_uarch-predicates.patch Patch58: xsa439-0010-x86-spec-ctrl-Mitigate-the-Zen1-DIV-leakage.patch +Patch59: xsa440-4.17.patch +Patch60: xsa442-4.17.patch +Patch61: xsa443-4.16-01.patch +Patch62: xsa443-4.16-02.patch +Patch63: xsa443-4.16-03.patch +Patch64: xsa443-4.16-04.patch +Patch65: xsa443-4.16-05.patch +Patch66: xsa443-4.16-06.patch +Patch67: xsa443-4.16-07.patch +Patch68: xsa443-4.16-08.patch +Patch69: xsa443-4.16-09.patch +Patch70: xsa443-4.16-10.patch +Patch71: xsa443-4.16-11.patch +Patch72: xsa444-4.16-1.patch +Patch73: xsa444-4.16-2.patch %if %build_qemutrad @@ -347,6 +362,21 @@ manage Xen virtual machines. %patch56 -p1 %patch57 -p1 %patch58 -p1 +%patch59 -p1 +%patch60 -p1 +%patch61 -p1 +%patch62 -p1 +%patch63 -p1 +%patch64 -p1 +%patch65 -p1 +%patch66 -p1 +%patch67 -p1 +%patch68 -p1 +%patch69 -p1 +%patch70 -p1 +%patch71 -p1 +%patch72 -p1 +%patch73 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -955,6 +985,15 @@ fi %endif %changelog +* Wed Oct 11 2023 Michael Young - 4.16.5-3 +- xenstored: A transaction conflict can crash C Xenstored [XSA-440, + CVE-2023-34323] +- x86/AMD: missing IOMMU TLB flushing [XSA-442, CVE-2023-34326] +- Multiple vulnerabilities in libfsimage disk handling [XSA-443, + CVE-2023-34325] +- x86/AMD: Debug Mask handling [XSA-444, CVE-2023-34327, + CVE-2023-34328] + * Tue Sep 26 2023 Michael Young - 4.16.5-2 - arm32: The cache may not be properly cleaned/invalidated [XSA-437, CVE-2023-34321] diff --git a/xsa440-4.17.patch b/xsa440-4.17.patch new file mode 100644 index 0000000..4941afc --- /dev/null +++ b/xsa440-4.17.patch @@ -0,0 +1,58 @@ +From 5d8b3d1ec98e56155d9650d7f4a70cd8ba9dc27d Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Fri, 22 Sep 2023 11:32:16 +0100 +Subject: tools/xenstored: domain_entry_fix(): Handle conflicting transaction + +The function domain_entry_fix() will be initially called to check if the +quota is correct before attempt to commit any nodes. So it would be +possible that accounting is temporarily negative. This is the case +in the following sequence: + + 1) Create 50 nodes + 2) Start two transactions + 3) Delete all the nodes in each transaction + 4) Commit the two transactions + +Because the first transaction will have succeed and updated the +accounting, there is no guarantee that 'd->nbentry + num' will still +be above 0. So the assert() would be triggered. +The assert() was introduced in dbef1f748289 ("tools/xenstore: simplify +and fix per domain node accounting") with the assumption that the +value can't be negative. As this is not true revert to the original +check but restricted to the path where we don't update. Take the +opportunity to explain the rationale behind the check. + +This CVE-2023-34323 / XSA-440. + +Reported-by: Stanislav Uschakow +Fixes: dbef1f748289 ("tools/xenstore: simplify and fix per domain node accounting") +Signed-off-by: Julien Grall +Reviewed-by: Juergen Gross + +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index aa86892fed9e..6074df210c6e 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -1094,10 +1094,20 @@ int domain_entry_fix(unsigned int domid, int num, bool update) + } + + cnt = d->nbentry + num; +- assert(cnt >= 0); + +- if (update) ++ if (update) { ++ assert(cnt >= 0); + d->nbentry = cnt; ++ } else if (cnt < 0) { ++ /* ++ * In a transaction when a node is being added/removed AND ++ * the same node has been added/removed outside the ++ * transaction in parallel, the result value may be negative. ++ * This is no problem, as the transaction will fail due to ++ * the resulting conflict. So override 'cnt'. ++ */ ++ cnt = 0; ++ } + + return domid_is_unprivileged(domid) ? cnt : 0; + } diff --git a/xsa442-4.17.patch b/xsa442-4.17.patch new file mode 100644 index 0000000..a78bfdd --- /dev/null +++ b/xsa442-4.17.patch @@ -0,0 +1,185 @@ +From 5b2ccb60ff22fbff44dd66214c2956a434ee6271 Mon Sep 17 00:00:00 2001 +From: Roger Pau Monne +Date: Tue, 13 Jun 2023 15:01:05 +0200 +Subject: [PATCH] iommu/amd-vi: flush IOMMU TLB when flushing the DTE +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The caching invalidation guidelines from the AMD-Vi specification (48882—Rev +3.07-PUB—Oct 2022) seem to be misleading on some hardware, as devices will +malfunction (see stale DMA mappings) if some fields of the DTE are updated but +the IOMMU TLB is not flushed. This has been observed in practice on AMD +systems. Due to the lack of guidance from the currently published +specification this patch aims to increase the flushing done in order to prevent +device malfunction. + +In order to fix, issue an INVALIDATE_IOMMU_PAGES command from +amd_iommu_flush_device(), flushing all the address space. Note this requires +callers to be adjusted in order to pass the DomID on the DTE previous to the +modification. + +Some call sites don't provide a valid DomID to amd_iommu_flush_device() in +order to avoid the flush. That's because the device had address translations +disabled and hence the previous DomID on the DTE is not valid. Note the +current logic relies on the entity disabling address translations to also flush +the TLB of the in use DomID. + +Device I/O TLB flushing when ATS are enabled is not covered by the current +change, as ATS usage is not security supported. + +This is XSA-442 / CVE-2023-34326 + +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +--- + xen/drivers/passthrough/amd/iommu.h | 3 ++- + xen/drivers/passthrough/amd/iommu_cmd.c | 10 +++++++++- + xen/drivers/passthrough/amd/iommu_guest.c | 5 +++-- + xen/drivers/passthrough/amd/iommu_init.c | 6 +++++- + xen/drivers/passthrough/amd/pci_amd_iommu.c | 14 ++++++++++---- + 5 files changed, 29 insertions(+), 9 deletions(-) + +diff --git a/xen/drivers/passthrough/amd/iommu.h b/xen/drivers/passthrough/amd/iommu.h +index 5429ada58ef5..a58be28bf96d 100644 +--- a/xen/drivers/passthrough/amd/iommu.h ++++ b/xen/drivers/passthrough/amd/iommu.h +@@ -283,7 +283,8 @@ void amd_iommu_flush_pages(struct domain *d, unsigned long dfn, + unsigned int order); + void amd_iommu_flush_iotlb(u8 devfn, const struct pci_dev *pdev, + uint64_t gaddr, unsigned int order); +-void amd_iommu_flush_device(struct amd_iommu *iommu, uint16_t bdf); ++void amd_iommu_flush_device(struct amd_iommu *iommu, uint16_t bdf, ++ domid_t domid); + void amd_iommu_flush_intremap(struct amd_iommu *iommu, uint16_t bdf); + void amd_iommu_flush_all_caches(struct amd_iommu *iommu); + +diff --git a/xen/drivers/passthrough/amd/iommu_cmd.c b/xen/drivers/passthrough/amd/iommu_cmd.c +index 40ddf366bb4d..cb28b36abc38 100644 +--- a/xen/drivers/passthrough/amd/iommu_cmd.c ++++ b/xen/drivers/passthrough/amd/iommu_cmd.c +@@ -363,10 +363,18 @@ void amd_iommu_flush_pages(struct domain *d, + _amd_iommu_flush_pages(d, __dfn_to_daddr(dfn), order); + } + +-void amd_iommu_flush_device(struct amd_iommu *iommu, uint16_t bdf) ++void amd_iommu_flush_device(struct amd_iommu *iommu, uint16_t bdf, ++ domid_t domid) + { + invalidate_dev_table_entry(iommu, bdf); + flush_command_buffer(iommu, 0); ++ ++ /* Also invalidate IOMMU TLB entries when flushing the DTE. */ ++ if ( domid != DOMID_INVALID ) ++ { ++ invalidate_iommu_pages(iommu, INV_IOMMU_ALL_PAGES_ADDRESS, domid, 0); ++ flush_command_buffer(iommu, 0); ++ } + } + + void amd_iommu_flush_intremap(struct amd_iommu *iommu, uint16_t bdf) +diff --git a/xen/drivers/passthrough/amd/iommu_guest.c b/xen/drivers/passthrough/amd/iommu_guest.c +index 80a331f546ed..be86bce6fb03 100644 +--- a/xen/drivers/passthrough/amd/iommu_guest.c ++++ b/xen/drivers/passthrough/amd/iommu_guest.c +@@ -385,7 +385,7 @@ static int do_completion_wait(struct domain *d, cmd_entry_t *cmd) + + static int do_invalidate_dte(struct domain *d, cmd_entry_t *cmd) + { +- uint16_t gbdf, mbdf, req_id, gdom_id, hdom_id; ++ uint16_t gbdf, mbdf, req_id, gdom_id, hdom_id, prev_domid; + struct amd_iommu_dte *gdte, *mdte, *dte_base; + struct amd_iommu *iommu = NULL; + struct guest_iommu *g_iommu; +@@ -445,13 +445,14 @@ static int do_invalidate_dte(struct domain *d, cmd_entry_t *cmd) + req_id = get_dma_requestor_id(iommu->seg, mbdf); + dte_base = iommu->dev_table.buffer; + mdte = &dte_base[req_id]; ++ prev_domid = mdte->domain_id; + + spin_lock_irqsave(&iommu->lock, flags); + dte_set_gcr3_table(mdte, hdom_id, gcr3_mfn << PAGE_SHIFT, gv, glx); + + spin_unlock_irqrestore(&iommu->lock, flags); + +- amd_iommu_flush_device(iommu, req_id); ++ amd_iommu_flush_device(iommu, req_id, prev_domid); + + return 0; + } +diff --git a/xen/drivers/passthrough/amd/iommu_init.c b/xen/drivers/passthrough/amd/iommu_init.c +index 166570648d26..101a60ce1794 100644 +--- a/xen/drivers/passthrough/amd/iommu_init.c ++++ b/xen/drivers/passthrough/amd/iommu_init.c +@@ -1547,7 +1547,11 @@ static int cf_check _invalidate_all_devices( + req_id = ivrs_mappings[bdf].dte_requestor_id; + if ( iommu ) + { +- amd_iommu_flush_device(iommu, req_id); ++ /* ++ * IOMMU TLB flush performed separately (see ++ * invalidate_all_domain_pages()). ++ */ ++ amd_iommu_flush_device(iommu, req_id, DOMID_INVALID); + amd_iommu_flush_intremap(iommu, req_id); + } + } +diff --git a/xen/drivers/passthrough/amd/pci_amd_iommu.c b/xen/drivers/passthrough/amd/pci_amd_iommu.c +index 94e37755064b..8641b84712a0 100644 +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -192,10 +192,13 @@ static int __must_check amd_iommu_setup_domain_device( + + spin_unlock_irqrestore(&iommu->lock, flags); + +- amd_iommu_flush_device(iommu, req_id); ++ /* DTE didn't have DMA translations enabled, do not flush the TLB. */ ++ amd_iommu_flush_device(iommu, req_id, DOMID_INVALID); + } + else if ( dte->pt_root != mfn_x(page_to_mfn(root_pg)) ) + { ++ domid_t prev_domid = dte->domain_id; ++ + /* + * Strictly speaking if the device is the only one with this requestor + * ID, it could be allowed to be re-assigned regardless of unity map +@@ -252,7 +255,7 @@ static int __must_check amd_iommu_setup_domain_device( + + spin_unlock_irqrestore(&iommu->lock, flags); + +- amd_iommu_flush_device(iommu, req_id); ++ amd_iommu_flush_device(iommu, req_id, prev_domid); + } + else + spin_unlock_irqrestore(&iommu->lock, flags); +@@ -421,6 +424,8 @@ static void amd_iommu_disable_domain_device(const struct domain *domain, + spin_lock_irqsave(&iommu->lock, flags); + if ( dte->tv || dte->v ) + { ++ domid_t prev_domid = dte->domain_id; ++ + /* See the comment in amd_iommu_setup_device_table(). */ + dte->int_ctl = IOMMU_DEV_TABLE_INT_CONTROL_ABORTED; + smp_wmb(); +@@ -439,7 +444,7 @@ static void amd_iommu_disable_domain_device(const struct domain *domain, + + spin_unlock_irqrestore(&iommu->lock, flags); + +- amd_iommu_flush_device(iommu, req_id); ++ amd_iommu_flush_device(iommu, req_id, prev_domid); + + AMD_IOMMU_DEBUG("Disable: device id = %#x, " + "domain = %d, paging mode = %d\n", +@@ -610,7 +615,8 @@ static int cf_check amd_iommu_add_device(u8 devfn, struct pci_dev *pdev) + + spin_unlock_irqrestore(&iommu->lock, flags); + +- amd_iommu_flush_device(iommu, bdf); ++ /* DTE didn't have DMA translations enabled, do not flush the TLB. */ ++ amd_iommu_flush_device(iommu, bdf, DOMID_INVALID); + } + + if ( amd_iommu_reserve_domain_unity_map( +-- +2.42.0 + diff --git a/xsa443-4.16-01.patch b/xsa443-4.16-01.patch new file mode 100644 index 0000000..48370d3 --- /dev/null +++ b/xsa443-4.16-01.patch @@ -0,0 +1,70 @@ +From c4d597f63832a53bbb1b826af7a4677e40e9fded Mon Sep 17 00:00:00 2001 +From: Alejandro Vallejo +Date: Thu, 14 Sep 2023 13:22:50 +0100 +Subject: [PATCH 01/11] libfsimage/xfs: Remove dead code + +xfs_info.agnolog (and related code) and XFS_INO_AGBNO_BITS are dead code +that serve no purpose. + +This is part of XSA-443 / CVE-2023-34325 + +Signed-off-by: Alejandro Vallejo +Reviewed-by: Jan Beulich +--- + tools/libfsimage/xfs/fsys_xfs.c | 18 ------------------ + 1 file changed, 18 deletions(-) + +diff --git a/tools/libfsimage/xfs/fsys_xfs.c b/tools/libfsimage/xfs/fsys_xfs.c +index d735a88e55f3..2800699f5985 100644 +--- a/tools/libfsimage/xfs/fsys_xfs.c ++++ b/tools/libfsimage/xfs/fsys_xfs.c +@@ -37,7 +37,6 @@ struct xfs_info { + int blklog; + int inopblog; + int agblklog; +- int agnolog; + unsigned int nextents; + xfs_daddr_t next; + xfs_daddr_t daddr; +@@ -65,9 +64,7 @@ static struct xfs_info xfs; + + #define XFS_INO_MASK(k) ((xfs_uint32_t)((1ULL << (k)) - 1)) + #define XFS_INO_OFFSET_BITS xfs.inopblog +-#define XFS_INO_AGBNO_BITS xfs.agblklog + #define XFS_INO_AGINO_BITS (xfs.agblklog + xfs.inopblog) +-#define XFS_INO_AGNO_BITS xfs.agnolog + + static inline xfs_agblock_t + agino2agbno (xfs_agino_t agino) +@@ -149,20 +146,6 @@ xt_len (xfs_bmbt_rec_32_t *r) + return le32(r->l3) & mask32lo(21); + } + +-static inline int +-xfs_highbit32(xfs_uint32_t v) +-{ +- int i; +- +- if (--v) { +- for (i = 0; i < 31; i++, v >>= 1) { +- if (v == 0) +- return i; +- } +- } +- return 0; +-} +- + static int + isinxt (xfs_fileoff_t key, xfs_fileoff_t offset, xfs_filblks_t len) + { +@@ -472,7 +455,6 @@ xfs_mount (fsi_file_t *ffi, const char *options) + + xfs.inopblog = super.sb_inopblog; + xfs.agblklog = super.sb_agblklog; +- xfs.agnolog = xfs_highbit32 (le32(super.sb_agcount)); + + xfs.btnode_ptr0_off = + ((xfs.bsize - sizeof(xfs_btree_block_t)) / +-- +2.42.0 + diff --git a/xsa443-4.16-02.patch b/xsa443-4.16-02.patch new file mode 100644 index 0000000..2acc03f --- /dev/null +++ b/xsa443-4.16-02.patch @@ -0,0 +1,32 @@ +From f75b0a70da392672fb7d9feed2a9e9515d74df2c Mon Sep 17 00:00:00 2001 +From: Alejandro Vallejo +Date: Thu, 14 Sep 2023 13:22:51 +0100 +Subject: [PATCH 02/11] libfsimage/xfs: Amend mask32lo() to allow the value 32 + +agblklog could plausibly be 32, but that would overflow this shift. +Perform the shift as ULL and cast to u32 at the end instead. + +This is part of XSA-443 / CVE-2023-34325 + +Signed-off-by: Alejandro Vallejo +Acked-by: Jan Beulich +--- + tools/libfsimage/xfs/fsys_xfs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/libfsimage/xfs/fsys_xfs.c b/tools/libfsimage/xfs/fsys_xfs.c +index 2800699f5985..4720bb4505c8 100644 +--- a/tools/libfsimage/xfs/fsys_xfs.c ++++ b/tools/libfsimage/xfs/fsys_xfs.c +@@ -60,7 +60,7 @@ static struct xfs_info xfs; + #define inode ((xfs_dinode_t *)((char *)FSYS_BUF + 8192)) + #define icore (inode->di_core) + +-#define mask32lo(n) (((xfs_uint32_t)1 << (n)) - 1) ++#define mask32lo(n) ((xfs_uint32_t)((1ull << (n)) - 1)) + + #define XFS_INO_MASK(k) ((xfs_uint32_t)((1ULL << (k)) - 1)) + #define XFS_INO_OFFSET_BITS xfs.inopblog +-- +2.42.0 + diff --git a/xsa443-4.16-03.patch b/xsa443-4.16-03.patch new file mode 100644 index 0000000..b0ad1fb --- /dev/null +++ b/xsa443-4.16-03.patch @@ -0,0 +1,137 @@ +From 25fae23b32ee4d990ae11368ee21e28e66dbfa25 Mon Sep 17 00:00:00 2001 +From: Alejandro Vallejo +Date: Thu, 14 Sep 2023 13:22:52 +0100 +Subject: [PATCH 03/11] libfsimage/xfs: Sanity-check the superblock during + mounts + +Sanity-check the XFS superblock for wellformedness at the mount handler. +This forces pygrub to abort parsing a potentially malformed filesystem and +ensures the invariants assumed throughout the rest of the code hold. + +Also, derive parameters from previously sanitized parameters where possible +(rather than reading them off the superblock) + +The code doesn't try to avoid overflowing the end of the disk, because +that's an unlikely and benign error. Parameters used in calculations of +xfs_daddr_t (like the root inode index) aren't in critical need of being +sanitized. + +The sanitization of agblklog is basically checking that no obvious +overflows happen on agblklog, and then ensuring agblocks is contained in +the range (2^(sb_agblklog-1), 2^sb_agblklog]. + +This is part of XSA-443 / CVE-2023-34325 + +Reported-by: Ferdinand Nölscher +Signed-off-by: Alejandro Vallejo +Reviewed-by: Jan Beulich +--- + tools/libfsimage/xfs/fsys_xfs.c | 48 ++++++++++++++++++++++++++------- + tools/libfsimage/xfs/xfs.h | 12 +++++++++ + 2 files changed, 50 insertions(+), 10 deletions(-) + +diff --git a/tools/libfsimage/xfs/fsys_xfs.c b/tools/libfsimage/xfs/fsys_xfs.c +index 4720bb4505c8..e4eb7e1ee26f 100644 +--- a/tools/libfsimage/xfs/fsys_xfs.c ++++ b/tools/libfsimage/xfs/fsys_xfs.c +@@ -17,6 +17,7 @@ + * along with this program; If not, see . + */ + ++#include + #include + #include "xfs.h" + +@@ -433,29 +434,56 @@ first_dentry (fsi_file_t *ffi, xfs_ino_t *ino) + return next_dentry (ffi, ino); + } + ++static bool ++xfs_sb_is_invalid (const xfs_sb_t *super) ++{ ++ return (le32(super->sb_magicnum) != XFS_SB_MAGIC) ++ || ((le16(super->sb_versionnum) & XFS_SB_VERSION_NUMBITS) != ++ XFS_SB_VERSION_4) ++ || (super->sb_inodelog < XFS_SB_INODELOG_MIN) ++ || (super->sb_inodelog > XFS_SB_INODELOG_MAX) ++ || (super->sb_blocklog < XFS_SB_BLOCKLOG_MIN) ++ || (super->sb_blocklog > XFS_SB_BLOCKLOG_MAX) ++ || (super->sb_blocklog < super->sb_inodelog) ++ || (super->sb_agblklog > XFS_SB_AGBLKLOG_MAX) ++ || ((1ull << super->sb_agblklog) < le32(super->sb_agblocks)) ++ || (((1ull << super->sb_agblklog) >> 1) >= ++ le32(super->sb_agblocks)) ++ || ((super->sb_blocklog + super->sb_dirblklog) >= ++ XFS_SB_DIRBLK_NUMBITS); ++} ++ + static int + xfs_mount (fsi_file_t *ffi, const char *options) + { + xfs_sb_t super; + + if (!devread (ffi, 0, 0, sizeof(super), (char *)&super) +- || (le32(super.sb_magicnum) != XFS_SB_MAGIC) +- || ((le16(super.sb_versionnum) +- & XFS_SB_VERSION_NUMBITS) != XFS_SB_VERSION_4) ) { ++ || xfs_sb_is_invalid(&super)) { + return 0; + } + +- xfs.bsize = le32 (super.sb_blocksize); +- xfs.blklog = super.sb_blocklog; +- xfs.bdlog = xfs.blklog - SECTOR_BITS; ++ /* ++ * Not sanitized. It's exclusively used to generate disk addresses, ++ * so it's not important from a security standpoint. ++ */ + xfs.rootino = le64 (super.sb_rootino); +- xfs.isize = le16 (super.sb_inodesize); +- xfs.agblocks = le32 (super.sb_agblocks); +- xfs.dirbsize = xfs.bsize << super.sb_dirblklog; + +- xfs.inopblog = super.sb_inopblog; ++ /* ++ * Sanitized to be consistent with each other, only used to ++ * generate disk addresses, so it's safe ++ */ ++ xfs.agblocks = le32 (super.sb_agblocks); + xfs.agblklog = super.sb_agblklog; + ++ /* Derived from sanitized parameters */ ++ xfs.bsize = 1 << super.sb_blocklog; ++ xfs.blklog = super.sb_blocklog; ++ xfs.bdlog = super.sb_blocklog - SECTOR_BITS; ++ xfs.isize = 1 << super.sb_inodelog; ++ xfs.dirbsize = 1 << (super.sb_blocklog + super.sb_dirblklog); ++ xfs.inopblog = super.sb_blocklog - super.sb_inodelog; ++ + xfs.btnode_ptr0_off = + ((xfs.bsize - sizeof(xfs_btree_block_t)) / + (sizeof (xfs_bmbt_key_t) + sizeof (xfs_bmbt_ptr_t))) +diff --git a/tools/libfsimage/xfs/xfs.h b/tools/libfsimage/xfs/xfs.h +index 40699281e44d..b87e37d3d7e9 100644 +--- a/tools/libfsimage/xfs/xfs.h ++++ b/tools/libfsimage/xfs/xfs.h +@@ -134,6 +134,18 @@ typedef struct xfs_sb + xfs_uint8_t sb_dummy[7]; /* padding */ + } xfs_sb_t; + ++/* Bound taken from xfs.c in GRUB2. It doesn't exist in the spec */ ++#define XFS_SB_DIRBLK_NUMBITS 27 ++/* Implied by the XFS specification. The minimum block size is 512 octets */ ++#define XFS_SB_BLOCKLOG_MIN 9 ++/* Implied by the XFS specification. The maximum block size is 65536 octets */ ++#define XFS_SB_BLOCKLOG_MAX 16 ++/* Implied by the XFS specification. The minimum inode size is 256 octets */ ++#define XFS_SB_INODELOG_MIN 8 ++/* Implied by the XFS specification. The maximum inode size is 2048 octets */ ++#define XFS_SB_INODELOG_MAX 11 ++/* High bound for sb_agblklog */ ++#define XFS_SB_AGBLKLOG_MAX 32 + + /* those are from xfs_btree.h */ + +-- +2.42.0 + diff --git a/xsa443-4.16-04.patch b/xsa443-4.16-04.patch new file mode 100644 index 0000000..1c23377 --- /dev/null +++ b/xsa443-4.16-04.patch @@ -0,0 +1,60 @@ +From e72c68e702dd930bc6013182bb44d3e8fbbb6bf4 Mon Sep 17 00:00:00 2001 +From: Alejandro Vallejo +Date: Thu, 14 Sep 2023 13:22:53 +0100 +Subject: [PATCH 04/11] libfsimage/xfs: Add compile-time check to libfsimage + +Adds the common tools include folder to the -I compile flags +of libfsimage. This allows us to use: + xen-tools/common-macros.h:BUILD_BUG_ON() + +With it, statically assert a sanitized "blocklog - SECTOR_BITS" cannot +underflow. + +This is part of XSA-443 / CVE-2023-34325 + +Signed-off-by: Alejandro Vallejo +Reviewed-by: Jan Beulich +--- + tools/libfsimage/Rules.mk | 2 +- + tools/libfsimage/xfs/fsys_xfs.c | 4 +++- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/tools/libfsimage/Rules.mk b/tools/libfsimage/Rules.mk +index bb6d42abb494..80598fb70aa7 100644 +--- a/tools/libfsimage/Rules.mk ++++ b/tools/libfsimage/Rules.mk +@@ -1,6 +1,6 @@ + include $(XEN_ROOT)/tools/Rules.mk + +-CFLAGS += -Wno-unknown-pragmas -I$(XEN_ROOT)/tools/libfsimage/common/ -DFSIMAGE_FSDIR=\"$(FSDIR)\" ++CFLAGS += -Wno-unknown-pragmas -I$(XEN_ROOT)/tools/libfsimage/common/ $(CFLAGS_xeninclude) -DFSIMAGE_FSDIR=\"$(FSDIR)\" + CFLAGS += -Werror -D_GNU_SOURCE + LDFLAGS += -L../common/ + +diff --git a/tools/libfsimage/xfs/fsys_xfs.c b/tools/libfsimage/xfs/fsys_xfs.c +index e4eb7e1ee26f..4a8dd6f2397b 100644 +--- a/tools/libfsimage/xfs/fsys_xfs.c ++++ b/tools/libfsimage/xfs/fsys_xfs.c +@@ -19,6 +19,7 @@ + + #include + #include ++#include + #include "xfs.h" + + #define MAX_LINK_COUNT 8 +@@ -477,9 +478,10 @@ xfs_mount (fsi_file_t *ffi, const char *options) + xfs.agblklog = super.sb_agblklog; + + /* Derived from sanitized parameters */ ++ BUILD_BUG_ON(XFS_SB_BLOCKLOG_MIN < SECTOR_BITS); ++ xfs.bdlog = super.sb_blocklog - SECTOR_BITS; + xfs.bsize = 1 << super.sb_blocklog; + xfs.blklog = super.sb_blocklog; +- xfs.bdlog = super.sb_blocklog - SECTOR_BITS; + xfs.isize = 1 << super.sb_inodelog; + xfs.dirbsize = 1 << (super.sb_blocklog + super.sb_dirblklog); + xfs.inopblog = super.sb_blocklog - super.sb_inodelog; +-- +2.42.0 + diff --git a/xsa443-4.16-05.patch b/xsa443-4.16-05.patch new file mode 100644 index 0000000..8bf9915 --- /dev/null +++ b/xsa443-4.16-05.patch @@ -0,0 +1,59 @@ +From 75fdc03c5a6b7fac0c3a5ac06a5beaac73aad36f Mon Sep 17 00:00:00 2001 +From: Alejandro Vallejo +Date: Mon, 25 Sep 2023 18:32:21 +0100 +Subject: [PATCH 05/11] tools/pygrub: Remove unnecessary hypercall + +There's a hypercall being issued in order to determine whether PV64 is +supported, but since Xen 4.3 that's strictly true so it's not required. + +Plus, this way we can avoid mapping the privcmd interface altogether in the +depriv pygrub. + +This is part of XSA-443 / CVE-2023-34325 + +Signed-off-by: Alejandro Vallejo +Reviewed-by: Andrew Cooper +--- + tools/pygrub/src/pygrub | 12 +----------- + 1 file changed, 1 insertion(+), 11 deletions(-) + +diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub +index ce7ab0eb8cf3..ce4e07d3e823 100755 +--- a/tools/pygrub/src/pygrub ++++ b/tools/pygrub/src/pygrub +@@ -18,7 +18,6 @@ import os, sys, string, struct, tempfile, re, traceback, stat, errno + import copy + import logging + import platform +-import xen.lowlevel.xc + + import curses, _curses, curses.textpad, curses.ascii + import getopt +@@ -668,14 +667,6 @@ def run_grub(file, entry, fs, cfg_args): + + return grubcfg + +-def supports64bitPVguest(): +- xc = xen.lowlevel.xc.xc() +- caps = xc.xeninfo()['xen_caps'].split(" ") +- for cap in caps: +- if cap == "xen-3.0-x86_64": +- return True +- return False +- + # If nothing has been specified, look for a Solaris domU. If found, perform the + # necessary tweaks. + def sniff_solaris(fs, cfg): +@@ -684,8 +675,7 @@ def sniff_solaris(fs, cfg): + return cfg + + if not cfg["kernel"]: +- if supports64bitPVguest() and \ +- fs.file_exists("/platform/i86xpv/kernel/amd64/unix"): ++ if fs.file_exists("/platform/i86xpv/kernel/amd64/unix"): + cfg["kernel"] = "/platform/i86xpv/kernel/amd64/unix" + cfg["ramdisk"] = "/platform/i86pc/amd64/boot_archive" + elif fs.file_exists("/platform/i86xpv/kernel/unix"): +-- +2.42.0 + diff --git a/xsa443-4.16-06.patch b/xsa443-4.16-06.patch new file mode 100644 index 0000000..cb18ee2 --- /dev/null +++ b/xsa443-4.16-06.patch @@ -0,0 +1,65 @@ +From 1083a16f63461e844e9515ac4d35d48bf55785af Mon Sep 17 00:00:00 2001 +From: Alejandro Vallejo +Date: Mon, 25 Sep 2023 18:32:22 +0100 +Subject: [PATCH 06/11] tools/pygrub: Small refactors + +Small tidy up to ensure output_directory always has a trailing '/' to ease +concatenating paths and that `output` can only be a filename or None. + +This is part of XSA-443 / CVE-2023-34325 + +Signed-off-by: Alejandro Vallejo +Acked-by: Andrew Cooper +--- + tools/pygrub/src/pygrub | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub +index ce4e07d3e823..1042c05b8676 100755 +--- a/tools/pygrub/src/pygrub ++++ b/tools/pygrub/src/pygrub +@@ -793,7 +793,7 @@ if __name__ == "__main__": + debug = False + not_really = False + output_format = "sxp" +- output_directory = "/var/run/xen/pygrub" ++ output_directory = "/var/run/xen/pygrub/" + + # what was passed in + incfg = { "kernel": None, "ramdisk": None, "args": "" } +@@ -815,7 +815,8 @@ if __name__ == "__main__": + usage() + sys.exit() + elif o in ("--output",): +- output = a ++ if a != "-": ++ output = a + elif o in ("--kernel",): + incfg["kernel"] = a + elif o in ("--ramdisk",): +@@ -847,12 +848,11 @@ if __name__ == "__main__": + if not os.path.isdir(a): + print("%s is not an existing directory" % a) + sys.exit(1) +- output_directory = a ++ output_directory = a + '/' + + if debug: + logging.basicConfig(level=logging.DEBUG) + +- + try: + os.makedirs(output_directory, 0o700) + except OSError as e: +@@ -861,7 +861,7 @@ if __name__ == "__main__": + else: + raise + +- if output is None or output == "-": ++ if output is None: + fd = sys.stdout.fileno() + else: + fd = os.open(output, os.O_WRONLY) +-- +2.42.0 + diff --git a/xsa443-4.16-07.patch b/xsa443-4.16-07.patch new file mode 100644 index 0000000..088c5eb --- /dev/null +++ b/xsa443-4.16-07.patch @@ -0,0 +1,105 @@ +From 350db30e33f39af40c1e3752d73c0a30ef2d26e7 Mon Sep 17 00:00:00 2001 +From: Alejandro Vallejo +Date: Mon, 25 Sep 2023 18:32:23 +0100 +Subject: [PATCH 07/11] tools/pygrub: Open the output files earlier + +This patch allows pygrub to get ahold of every RW file descriptor it needs +early on. A later patch will clamp the filesystem it can access so it can't +obtain any others. + +This is part of XSA-443 / CVE-2023-34325 + +Signed-off-by: Alejandro Vallejo +Acked-by: Andrew Cooper +--- + tools/pygrub/src/pygrub | 37 ++++++++++++++++++++++--------------- + 1 file changed, 22 insertions(+), 15 deletions(-) + +diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub +index 1042c05b8676..91e2ec2ab105 100755 +--- a/tools/pygrub/src/pygrub ++++ b/tools/pygrub/src/pygrub +@@ -738,8 +738,7 @@ if __name__ == "__main__": + def usage(): + print("Usage: %s [-q|--quiet] [-i|--interactive] [-l|--list-entries] [-n|--not-really] [--output=] [--kernel=] [--ramdisk=] [--args=] [--entry=] [--output-directory=] [--output-format=sxp|simple|simple0] [--offset=] " %(sys.argv[0],), file=sys.stderr) + +- def copy_from_image(fs, file_to_read, file_type, output_directory, +- not_really): ++ def copy_from_image(fs, file_to_read, file_type, fd_dst, path_dst, not_really): + if not_really: + if fs.file_exists(file_to_read): + return "<%s:%s>" % (file_type, file_to_read) +@@ -750,21 +749,18 @@ if __name__ == "__main__": + except Exception as e: + print(e, file=sys.stderr) + sys.exit("Error opening %s in guest" % file_to_read) +- (tfd, ret) = tempfile.mkstemp(prefix="boot_"+file_type+".", +- dir=output_directory) + dataoff = 0 + while True: + data = datafile.read(FS_READ_MAX, dataoff) + if len(data) == 0: +- os.close(tfd) ++ os.close(fd_dst) + del datafile +- return ret ++ return + try: +- os.write(tfd, data) ++ os.write(fd_dst, data) + except Exception as e: + print(e, file=sys.stderr) +- os.close(tfd) +- os.unlink(ret) ++ os.unlink(path_dst) + del datafile + sys.exit("Error writing temporary copy of "+file_type) + dataoff += len(data) +@@ -861,6 +857,14 @@ if __name__ == "__main__": + else: + raise + ++ if not_really: ++ fd_kernel = path_kernel = fd_ramdisk = path_ramdisk = None ++ else: ++ (fd_kernel, path_kernel) = tempfile.mkstemp(prefix="boot_kernel.", ++ dir=output_directory) ++ (fd_ramdisk, path_ramdisk) = tempfile.mkstemp(prefix="boot_ramdisk.", ++ dir=output_directory) ++ + if output is None: + fd = sys.stdout.fileno() + else: +@@ -920,20 +924,23 @@ if __name__ == "__main__": + if fs is None: + raise RuntimeError("Unable to find partition containing kernel") + +- bootcfg["kernel"] = copy_from_image(fs, chosencfg["kernel"], "kernel", +- output_directory, not_really) ++ copy_from_image(fs, chosencfg["kernel"], "kernel", ++ fd_kernel, path_kernel, not_really) ++ bootcfg["kernel"] = path_kernel + + if chosencfg["ramdisk"]: + try: +- bootcfg["ramdisk"] = copy_from_image(fs, chosencfg["ramdisk"], +- "ramdisk", output_directory, +- not_really) ++ copy_from_image(fs, chosencfg["ramdisk"], "ramdisk", ++ fd_ramdisk, path_ramdisk, not_really) + except: + if not not_really: +- os.unlink(bootcfg["kernel"]) ++ os.unlink(path_kernel) + raise ++ bootcfg["ramdisk"] = path_ramdisk + else: + initrd = None ++ if not not_really: ++ os.unlink(path_ramdisk) + + args = None + if chosencfg["args"]: +-- +2.42.0 + diff --git a/xsa443-4.16-08.patch b/xsa443-4.16-08.patch new file mode 100644 index 0000000..40c28e1 --- /dev/null +++ b/xsa443-4.16-08.patch @@ -0,0 +1,126 @@ +From 1548ad2291ec7a72ae6949c11d2e50cea135a48d Mon Sep 17 00:00:00 2001 +From: Alejandro Vallejo +Date: Mon, 25 Sep 2023 18:32:24 +0100 +Subject: [PATCH 08/11] tools/libfsimage: Export a new function to preload all + plugins + +This is work required in order to let pygrub operate in highly deprivileged +chroot mode. This patch adds a function that preloads every plugin, hence +ensuring that a on function exit, every shared library is loaded in memory. + +The new "init" function is supposed to be used before depriv, but that's +fine because it's not acting on untrusted data. + +This is part of XSA-443 / CVE-2023-34325 + +Signed-off-by: Alejandro Vallejo +Acked-by: Andrew Cooper +--- + tools/libfsimage/common/fsimage_plugin.c | 4 ++-- + tools/libfsimage/common/mapfile-GNU | 1 + + tools/libfsimage/common/mapfile-SunOS | 1 + + tools/libfsimage/common/xenfsimage.h | 8 ++++++++ + tools/pygrub/src/fsimage/fsimage.c | 15 +++++++++++++++ + 5 files changed, 27 insertions(+), 2 deletions(-) + +diff --git a/tools/libfsimage/common/fsimage_plugin.c b/tools/libfsimage/common/fsimage_plugin.c +index de1412b4233a..d0cb9e96a654 100644 +--- a/tools/libfsimage/common/fsimage_plugin.c ++++ b/tools/libfsimage/common/fsimage_plugin.c +@@ -119,7 +119,7 @@ fail: + return (-1); + } + +-static int load_plugins(void) ++int fsi_init(void) + { + const char *fsdir = getenv("XEN_FSIMAGE_FSDIR"); + struct dirent *dp = NULL; +@@ -180,7 +180,7 @@ int find_plugin(fsi_t *fsi, const char *path, const char *options) + fsi_plugin_t *fp; + int ret = 0; + +- if (plugins == NULL && (ret = load_plugins()) != 0) ++ if (plugins == NULL && (ret = fsi_init()) != 0) + goto out; + + for (fp = plugins; fp != NULL; fp = fp->fp_next) { +diff --git a/tools/libfsimage/common/mapfile-GNU b/tools/libfsimage/common/mapfile-GNU +index 26d4d7a69ec7..2d54d527d7f5 100644 +--- a/tools/libfsimage/common/mapfile-GNU ++++ b/tools/libfsimage/common/mapfile-GNU +@@ -1,6 +1,7 @@ + VERSION { + libfsimage.so.1.0 { + global: ++ fsi_init; + fsi_open_fsimage; + fsi_close_fsimage; + fsi_file_exists; +diff --git a/tools/libfsimage/common/mapfile-SunOS b/tools/libfsimage/common/mapfile-SunOS +index e99b90b65077..48deedb4252f 100644 +--- a/tools/libfsimage/common/mapfile-SunOS ++++ b/tools/libfsimage/common/mapfile-SunOS +@@ -1,5 +1,6 @@ + libfsimage.so.1.0 { + global: ++ fsi_init; + fsi_open_fsimage; + fsi_close_fsimage; + fsi_file_exists; +diff --git a/tools/libfsimage/common/xenfsimage.h b/tools/libfsimage/common/xenfsimage.h +index 201abd54f23a..341883b2d71a 100644 +--- a/tools/libfsimage/common/xenfsimage.h ++++ b/tools/libfsimage/common/xenfsimage.h +@@ -35,6 +35,14 @@ extern C { + typedef struct fsi fsi_t; + typedef struct fsi_file fsi_file_t; + ++/* ++ * Optional initialization function. If invoked it loads the associated ++ * dynamic libraries for the backends ahead of time. This is required if ++ * the library is to run as part of a highly deprivileged executable, as ++ * the libraries may not be reachable after depriv. ++ */ ++int fsi_init(void); ++ + fsi_t *fsi_open_fsimage(const char *, uint64_t, const char *); + void fsi_close_fsimage(fsi_t *); + +diff --git a/tools/pygrub/src/fsimage/fsimage.c b/tools/pygrub/src/fsimage/fsimage.c +index 2ebbbe35df92..92fbf2851f01 100644 +--- a/tools/pygrub/src/fsimage/fsimage.c ++++ b/tools/pygrub/src/fsimage/fsimage.c +@@ -286,6 +286,15 @@ fsimage_getbootstring(PyObject *o, PyObject *args) + return Py_BuildValue("s", bootstring); + } + ++static PyObject * ++fsimage_init(PyObject *o, PyObject *args) ++{ ++ if (!PyArg_ParseTuple(args, "")) ++ return (NULL); ++ ++ return Py_BuildValue("i", fsi_init()); ++} ++ + PyDoc_STRVAR(fsimage_open__doc__, + "open(name, [offset=off]) - Open the given file as a filesystem image.\n" + "\n" +@@ -297,7 +306,13 @@ PyDoc_STRVAR(fsimage_getbootstring__doc__, + "getbootstring(fs) - Return the boot string needed for this file system " + "or NULL if none is needed.\n"); + ++PyDoc_STRVAR(fsimage_init__doc__, ++ "init() - Loads every dynamic library contained in xenfsimage " ++ "into memory so that it can be used in chrooted environments.\n"); ++ + static struct PyMethodDef fsimage_module_methods[] = { ++ { "init", (PyCFunction)fsimage_init, ++ METH_VARARGS, fsimage_init__doc__ }, + { "open", (PyCFunction)fsimage_open, + METH_VARARGS|METH_KEYWORDS, fsimage_open__doc__ }, + { "getbootstring", (PyCFunction)fsimage_getbootstring, +-- +2.42.0 + diff --git a/xsa443-4.16-09.patch b/xsa443-4.16-09.patch new file mode 100644 index 0000000..77d0d66 --- /dev/null +++ b/xsa443-4.16-09.patch @@ -0,0 +1,307 @@ +From 4d331b0b914dfc17bd2d883bc55aeb798930832a Mon Sep 17 00:00:00 2001 +From: Alejandro Vallejo +Date: Mon, 25 Sep 2023 18:32:25 +0100 +Subject: [PATCH 09/11] tools/pygrub: Deprivilege pygrub + +Introduce a --runas= flag to deprivilege pygrub on Linux and *BSDs. It +also implicitly creates a chroot env where it drops a deprivileged forked +process. The chroot itself is cleaned up at the end. + +If the --runas arg is present, then pygrub forks, leaving the child to +deprivilege itself, and waiting for it to complete. When the child exists, +the parent performs cleanup and exits with the same error code. + +This is roughly what the child does: + 1. Initialize libfsimage (this loads every .so in memory so the chroot + can avoid bind-mounting /{,usr}/lib* + 2. Create a temporary empty chroot directory + 3. Mount tmpfs in it + 4. Bind mount the disk inside, because libfsimage expects a path, not a + file descriptor. + 5. Remount the root tmpfs to be stricter (ro,nosuid,nodev) + 6. Set RLIMIT_FSIZE to a sensibly high amount (128 MiB) + 7. Depriv gid, groups and uid + +With this scheme in place, the "output" files are writable (up to +RLIMIT_FSIZE octets) and the exposed filesystem is immutable and contains +the single only file we can't easily get rid of (the disk). + +If running on Linux, the child process also unshares mount, IPC, and +network namespaces before dropping its privileges. + +This is part of XSA-443 / CVE-2023-34325 + +Signed-off-by: Alejandro Vallejo +Acked-by: Andrew Cooper +--- + tools/pygrub/setup.py | 2 +- + tools/pygrub/src/pygrub | 162 +++++++++++++++++++++++++++++++++++++--- + 2 files changed, 154 insertions(+), 10 deletions(-) + +diff --git a/tools/pygrub/setup.py b/tools/pygrub/setup.py +index b8f1dc4590cf..f16187b6d118 100644 +--- a/tools/pygrub/setup.py ++++ b/tools/pygrub/setup.py +@@ -17,7 +17,7 @@ xenfsimage = Extension("xenfsimage", + pkgs = [ 'grub' ] + + setup(name='pygrub', +- version='0.6', ++ version='0.7', + description='Boot loader that looks a lot like grub for Xen', + author='Jeremy Katz', + author_email='katzj@redhat.com', +diff --git a/tools/pygrub/src/pygrub b/tools/pygrub/src/pygrub +index 91e2ec2ab105..7cea496ade08 100755 +--- a/tools/pygrub/src/pygrub ++++ b/tools/pygrub/src/pygrub +@@ -16,8 +16,11 @@ from __future__ import print_function + + import os, sys, string, struct, tempfile, re, traceback, stat, errno + import copy ++import ctypes, ctypes.util + import logging + import platform ++import resource ++import subprocess + + import curses, _curses, curses.textpad, curses.ascii + import getopt +@@ -27,10 +30,135 @@ import grub.GrubConf + import grub.LiloConf + import grub.ExtLinuxConf + +-PYGRUB_VER = 0.6 ++PYGRUB_VER = 0.7 + FS_READ_MAX = 1024 * 1024 + SECTOR_SIZE = 512 + ++# Unless provided through the env variable PYGRUB_MAX_FILE_SIZE_MB, then ++# this is the maximum filesize allowed for files written by the depriv ++# pygrub ++LIMIT_FSIZE = 128 << 20 ++ ++CLONE_NEWNS = 0x00020000 # mount namespace ++CLONE_NEWNET = 0x40000000 # network namespace ++CLONE_NEWIPC = 0x08000000 # IPC namespace ++ ++def unshare(flags): ++ if not sys.platform.startswith("linux"): ++ print("skip_unshare reason=not_linux platform=%s", sys.platform, file=sys.stderr) ++ return ++ ++ libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True) ++ unshare_prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, use_errno=True) ++ unshare = unshare_prototype(('unshare', libc)) ++ ++ if unshare(flags) < 0: ++ raise OSError(ctypes.get_errno(), os.strerror(ctypes.get_errno())) ++ ++def bind_mount(src, dst, options): ++ open(dst, "a").close() # touch ++ ++ rc = subprocess.call(["mount", "--bind", "-o", options, src, dst]) ++ if rc != 0: ++ raise RuntimeError("bad_mount: src=%s dst=%s opts=%s" % ++ (src, dst, options)) ++ ++def downgrade_rlimits(): ++ # Wipe the authority to use unrequired resources ++ resource.setrlimit(resource.RLIMIT_NPROC, (0, 0)) ++ resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) ++ resource.setrlimit(resource.RLIMIT_MEMLOCK, (0, 0)) ++ ++ # py2's resource module doesn't know about resource.RLIMIT_MSGQUEUE ++ # ++ # TODO: Use resource.RLIMIT_MSGQUEUE after python2 is deprecated ++ if sys.platform.startswith('linux'): ++ RLIMIT_MSGQUEUE = 12 ++ resource.setrlimit(RLIMIT_MSGQUEUE, (0, 0)) ++ ++ # The final look of the filesystem for this process is fully RO, but ++ # note we have some file descriptor already open (notably, kernel and ++ # ramdisk). In order to avoid a compromised pygrub from filling up the ++ # filesystem we set RLIMIT_FSIZE to a high bound, so that the file ++ # write permissions are bound. ++ fsize = LIMIT_FSIZE ++ if "PYGRUB_MAX_FILE_SIZE_MB" in os.environ.keys(): ++ fsize = os.environ["PYGRUB_MAX_FILE_SIZE_MB"] << 20 ++ ++ resource.setrlimit(resource.RLIMIT_FSIZE, (fsize, fsize)) ++ ++def depriv(output_directory, output, device, uid, path_kernel, path_ramdisk): ++ # The only point of this call is to force the loading of libfsimage. ++ # That way, we don't need to bind-mount it into the chroot ++ rc = xenfsimage.init() ++ if rc != 0: ++ os.unlink(path_ramdisk) ++ os.unlink(path_kernel) ++ raise RuntimeError("bad_xenfsimage: rc=%d" % rc) ++ ++ # Create a temporary directory for the chroot ++ chroot = tempfile.mkdtemp(prefix=str(uid)+'-', dir=output_directory) + '/' ++ device_path = '/device' ++ ++ pid = os.fork() ++ if pid: ++ # parent ++ _, rc = os.waitpid(pid, 0) ++ ++ for path in [path_kernel, path_ramdisk]: ++ # If the child didn't write anything, just get rid of it, ++ # otherwise we end up consuming a 0-size file when parsing ++ # systems without a ramdisk that the ultimate caller of pygrub ++ # may just be unaware of ++ if rc != 0 or os.path.getsize(path) == 0: ++ os.unlink(path) ++ ++ # Normally, unshare(CLONE_NEWNS) will ensure this is not required. ++ # However, this syscall doesn't exist in *BSD systems and doesn't ++ # auto-unmount everything on older Linux kernels (At least as of ++ # Linux 4.19, but it seems fixed in 5.15). Either way, ++ # recursively unmount everything if needed. Quietly. ++ with open('/dev/null', 'w') as devnull: ++ subprocess.call(["umount", "-f", chroot + device_path], ++ stdout=devnull, stderr=devnull) ++ subprocess.call(["umount", "-f", chroot], ++ stdout=devnull, stderr=devnull) ++ os.rmdir(chroot) ++ ++ sys.exit(rc) ++ ++ # By unsharing the namespace we're making sure it's all bulk-released ++ # at the end, when the namespaces disappear. This means the kernel does ++ # (almost) all the cleanup for us and the parent just has to remove the ++ # temporary directory. ++ unshare(CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWNET) ++ ++ # Set sensible limits using the setrlimit interface ++ downgrade_rlimits() ++ ++ # We'll mount tmpfs on the chroot to ensure the deprivileged child ++ # cannot affect the persistent state. It's RW now in order to ++ # bind-mount the device, but note it's remounted RO after that. ++ rc = subprocess.call(["mount", "-t", "tmpfs", "none", chroot]) ++ if rc != 0: ++ raise RuntimeError("mount_tmpfs rc=%d dst=\"%s\"" % (rc, chroot)) ++ ++ # Bind the untrusted device RO ++ bind_mount(device, chroot + device_path, "ro,nosuid,noexec") ++ ++ rc = subprocess.call(["mount", "-t", "tmpfs", "-o", "remount,ro,nosuid,noexec,nodev", "none", chroot]) ++ if rc != 0: ++ raise RuntimeError("remount_tmpfs rc=%d dst=\"%s\"" % (rc, chroot)) ++ ++ # Drop superpowers! ++ os.chroot(chroot) ++ os.chdir('/') ++ os.setgid(uid) ++ os.setgroups([uid]) ++ os.setuid(uid) ++ ++ return device_path ++ + def read_size_roundup(fd, size): + if platform.system() != 'FreeBSD': + return size +@@ -736,7 +864,7 @@ if __name__ == "__main__": + sel = None + + def usage(): +- print("Usage: %s [-q|--quiet] [-i|--interactive] [-l|--list-entries] [-n|--not-really] [--output=] [--kernel=] [--ramdisk=] [--args=] [--entry=] [--output-directory=] [--output-format=sxp|simple|simple0] [--offset=] " %(sys.argv[0],), file=sys.stderr) ++ print("Usage: %s [-q|--quiet] [-i|--interactive] [-l|--list-entries] [-n|--not-really] [--output=] [--kernel=] [--ramdisk=] [--args=] [--entry=] [--output-directory=] [--output-format=sxp|simple|simple0] [--runas=] [--offset=] " %(sys.argv[0],), file=sys.stderr) + + def copy_from_image(fs, file_to_read, file_type, fd_dst, path_dst, not_really): + if not_really: +@@ -760,7 +888,8 @@ if __name__ == "__main__": + os.write(fd_dst, data) + except Exception as e: + print(e, file=sys.stderr) +- os.unlink(path_dst) ++ if path_dst: ++ os.unlink(path_dst) + del datafile + sys.exit("Error writing temporary copy of "+file_type) + dataoff += len(data) +@@ -769,7 +898,7 @@ if __name__ == "__main__": + opts, args = getopt.gnu_getopt(sys.argv[1:], 'qilnh::', + ["quiet", "interactive", "list-entries", "not-really", "help", + "output=", "output-format=", "output-directory=", "offset=", +- "entry=", "kernel=", ++ "runas=", "entry=", "kernel=", + "ramdisk=", "args=", "isconfig", "debug"]) + except getopt.GetoptError: + usage() +@@ -790,6 +919,7 @@ if __name__ == "__main__": + not_really = False + output_format = "sxp" + output_directory = "/var/run/xen/pygrub/" ++ uid = None + + # what was passed in + incfg = { "kernel": None, "ramdisk": None, "args": "" } +@@ -813,6 +943,13 @@ if __name__ == "__main__": + elif o in ("--output",): + if a != "-": + output = a ++ elif o in ("--runas",): ++ try: ++ uid = int(a) ++ except ValueError: ++ print("runas value must be an integer user id") ++ usage() ++ sys.exit(1) + elif o in ("--kernel",): + incfg["kernel"] = a + elif o in ("--ramdisk",): +@@ -849,6 +986,10 @@ if __name__ == "__main__": + if debug: + logging.basicConfig(level=logging.DEBUG) + ++ if interactive and uid: ++ print("In order to use --runas, you must also set --entry or -q", file=sys.stderr) ++ sys.exit(1) ++ + try: + os.makedirs(output_directory, 0o700) + except OSError as e: +@@ -870,6 +1011,9 @@ if __name__ == "__main__": + else: + fd = os.open(output, os.O_WRONLY) + ++ if uid: ++ file = depriv(output_directory, output, file, uid, path_kernel, path_ramdisk) ++ + # debug + if isconfig: + chosencfg = run_grub(file, entry, fs, incfg["args"]) +@@ -925,21 +1069,21 @@ if __name__ == "__main__": + raise RuntimeError("Unable to find partition containing kernel") + + copy_from_image(fs, chosencfg["kernel"], "kernel", +- fd_kernel, path_kernel, not_really) ++ fd_kernel, None if uid else path_kernel, not_really) + bootcfg["kernel"] = path_kernel + + if chosencfg["ramdisk"]: + try: + copy_from_image(fs, chosencfg["ramdisk"], "ramdisk", +- fd_ramdisk, path_ramdisk, not_really) ++ fd_ramdisk, None if uid else path_ramdisk, not_really) + except: +- if not not_really: +- os.unlink(path_kernel) ++ if not uid and not not_really: ++ os.unlink(path_kernel) + raise + bootcfg["ramdisk"] = path_ramdisk + else: + initrd = None +- if not not_really: ++ if not uid and not not_really: + os.unlink(path_ramdisk) + + args = None +-- +2.42.0 + diff --git a/xsa443-4.16-10.patch b/xsa443-4.16-10.patch new file mode 100644 index 0000000..7c65748 --- /dev/null +++ b/xsa443-4.16-10.patch @@ -0,0 +1,250 @@ +From a5be7e8b054f586ad934e786232af29fdc6e3ead Mon Sep 17 00:00:00 2001 +From: Roger Pau Monne +Date: Mon, 25 Sep 2023 14:30:20 +0200 +Subject: [PATCH 10/11] libxl: add support for running bootloader in restricted + mode +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Much like the device model depriv mode, add the same kind of support for the +bootloader. Such feature allows passing a UID as a parameter for the +bootloader to run as, together with the bootloader itself taking the necessary +actions to isolate. + +Note that the user to run the bootloader as must have the right permissions to +access the guest disk image (in read mode only), and that the bootloader will +be run in non-interactive mode when restricted. + +If enabled bootloader restrict mode will attempt to re-use the user(s) from the +QEMU depriv implementation if no user is provided on the configuration file or +the environment. See docs/features/qemu-deprivilege.pandoc for more +information about how to setup those users. + +Bootloader restrict mode is not enabled by default as it requires certain +setup to be done first (setup of the user(s) to use in restrict mode). + +This is part of XSA-443 / CVE-2023-34325 + +Signed-off-by: Roger Pau Monné +Reviewed-by: Anthony PERARD +--- + docs/man/xl.1.pod.in | 33 +++++++++++ + tools/libs/light/libxl_bootloader.c | 89 ++++++++++++++++++++++++++++- + tools/libs/light/libxl_dm.c | 8 +-- + tools/libs/light/libxl_internal.h | 8 +++ + 4 files changed, 131 insertions(+), 7 deletions(-) + +diff --git a/docs/man/xl.1.pod.in b/docs/man/xl.1.pod.in +index 45e1430aeb74..96e6fb1c32a3 100644 +--- a/docs/man/xl.1.pod.in ++++ b/docs/man/xl.1.pod.in +@@ -1976,6 +1976,39 @@ ignored: + + =back + ++=head1 ENVIRONMENT VARIABLES ++ ++The following environment variables shall affect the execution of xl: ++ ++=over 4 ++ ++=item LIBXL_BOOTLOADER_RESTRICT ++ ++Attempt to restrict the bootloader after startup, to limit the ++consequences of security vulnerabilities due to parsing guest ++owned image files. ++ ++See docs/features/qemu-deprivilege.pandoc for more information ++on how to setup the unprivileged users. ++ ++Note that running the bootloader in restricted mode also implies using ++non-interactive mode, and the disk image must be readable by the ++restricted user. ++ ++Having this variable set is equivalent to enabling the option, even if the ++value is 0. ++ ++=item LIBXL_BOOTLOADER_USER ++ ++When using bootloader_restrict, run the bootloader as this user. If ++not set the default QEMU restrict users will be used. ++ ++NOTE: Each domain MUST have a SEPARATE username. ++ ++See docs/features/qemu-deprivilege.pandoc for more information. ++ ++=back ++ + =head1 SEE ALSO + + The following man pages: +diff --git a/tools/libs/light/libxl_bootloader.c b/tools/libs/light/libxl_bootloader.c +index 1bc6e51827b9..d3a8a4a9ba59 100644 +--- a/tools/libs/light/libxl_bootloader.c ++++ b/tools/libs/light/libxl_bootloader.c +@@ -14,6 +14,7 @@ + + #include "libxl_osdeps.h" /* must come before any other headers */ + ++#include + #include + #ifdef HAVE_UTMP_H + #include +@@ -42,8 +43,71 @@ static void bootloader_arg(libxl__bootloader_state *bl, const char *arg) + bl->args[bl->nargs++] = arg; + } + +-static void make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl, +- const char *bootloader_path) ++static int bootloader_uid(libxl__gc *gc, domid_t guest_domid, ++ const char *user, uid_t *intended_uid) ++{ ++ struct passwd *user_base, user_pwbuf; ++ int rc; ++ ++ if (user) { ++ rc = userlookup_helper_getpwnam(gc, user, &user_pwbuf, &user_base); ++ if (rc) return rc; ++ ++ if (!user_base) { ++ LOGD(ERROR, guest_domid, "Couldn't find user %s", user); ++ return ERROR_INVAL; ++ } ++ ++ *intended_uid = user_base->pw_uid; ++ return 0; ++ } ++ ++ /* Re-use QEMU user range for the bootloader. */ ++ rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_RANGE_BASE, ++ &user_pwbuf, &user_base); ++ if (rc) return rc; ++ ++ if (user_base) { ++ struct passwd *user_clash, user_clash_pwbuf; ++ uid_t temp_uid = user_base->pw_uid + guest_domid; ++ ++ rc = userlookup_helper_getpwuid(gc, temp_uid, &user_clash_pwbuf, ++ &user_clash); ++ if (rc) return rc; ++ ++ if (user_clash) { ++ LOGD(ERROR, guest_domid, ++ "wanted to use uid %ld (%s + %d) but that is user %s !", ++ (long)temp_uid, LIBXL_QEMU_USER_RANGE_BASE, ++ guest_domid, user_clash->pw_name); ++ return ERROR_INVAL; ++ } ++ ++ *intended_uid = temp_uid; ++ return 0; ++ } ++ ++ rc = userlookup_helper_getpwnam(gc, LIBXL_QEMU_USER_SHARED, &user_pwbuf, ++ &user_base); ++ if (rc) return rc; ++ ++ if (user_base) { ++ LOGD(WARN, guest_domid, "Could not find user %s, falling back to %s", ++ LIBXL_QEMU_USER_RANGE_BASE, LIBXL_QEMU_USER_SHARED); ++ *intended_uid = user_base->pw_uid; ++ ++ return 0; ++ } ++ ++ LOGD(ERROR, guest_domid, ++ "Could not find user %s or range base pseudo-user %s, cannot restrict", ++ LIBXL_QEMU_USER_SHARED, LIBXL_QEMU_USER_RANGE_BASE); ++ ++ return ERROR_INVAL; ++} ++ ++static int make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl, ++ const char *bootloader_path) + { + const libxl_domain_build_info *info = bl->info; + +@@ -61,6 +125,23 @@ static void make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl, + ARG(GCSPRINTF("--ramdisk=%s", info->ramdisk)); + if (info->cmdline && *info->cmdline != '\0') + ARG(GCSPRINTF("--args=%s", info->cmdline)); ++ if (getenv("LIBXL_BOOTLOADER_RESTRICT") || ++ getenv("LIBXL_BOOTLOADER_USER")) { ++ uid_t uid = -1; ++ int rc = bootloader_uid(gc, bl->domid, getenv("LIBXL_BOOTLOADER_USER"), ++ &uid); ++ ++ if (rc) return rc; ++ ++ assert(uid != -1); ++ if (!uid) { ++ LOGD(ERROR, bl->domid, "bootloader restrict UID is 0 (root)!"); ++ return ERROR_INVAL; ++ } ++ LOGD(DEBUG, bl->domid, "using uid %ld", (long)uid); ++ ARG(GCSPRINTF("--runas=%ld", (long)uid)); ++ ARG("--quiet"); ++ } + + ARG(GCSPRINTF("--output=%s", bl->outputpath)); + ARG("--output-format=simple0"); +@@ -79,6 +160,7 @@ static void make_bootloader_args(libxl__gc *gc, libxl__bootloader_state *bl, + /* Sentinel for execv */ + ARG(NULL); + ++ return 0; + #undef ARG + } + +@@ -443,7 +525,8 @@ static void bootloader_disk_attached_cb(libxl__egc *egc, + bootloader = bltmp; + } + +- make_bootloader_args(gc, bl, bootloader); ++ rc = make_bootloader_args(gc, bl, bootloader); ++ if (rc) goto out; + + bl->openpty.ao = ao; + bl->openpty.callback = bootloader_gotptys; +diff --git a/tools/libs/light/libxl_dm.c b/tools/libs/light/libxl_dm.c +index fc264a3a13a6..14b593110f7c 100644 +--- a/tools/libs/light/libxl_dm.c ++++ b/tools/libs/light/libxl_dm.c +@@ -80,10 +80,10 @@ static int libxl__create_qemu_logfile(libxl__gc *gc, char *name) + * On error, return a libxl-style error code. + */ + #define DEFINE_USERLOOKUP_HELPER(NAME,SPEC_TYPE,STRUCTNAME,SYSCONF) \ +- static int userlookup_helper_##NAME(libxl__gc *gc, \ +- SPEC_TYPE spec, \ +- struct STRUCTNAME *resultbuf, \ +- struct STRUCTNAME **out) \ ++ int userlookup_helper_##NAME(libxl__gc *gc, \ ++ SPEC_TYPE spec, \ ++ struct STRUCTNAME *resultbuf, \ ++ struct STRUCTNAME **out) \ + { \ + struct STRUCTNAME *resultp = NULL; \ + char *buf = NULL; \ +diff --git a/tools/libs/light/libxl_internal.h b/tools/libs/light/libxl_internal.h +index cc27c72ecf30..8415d1feed16 100644 +--- a/tools/libs/light/libxl_internal.h ++++ b/tools/libs/light/libxl_internal.h +@@ -4864,6 +4864,14 @@ struct libxl__cpu_policy { + struct xc_msr *msr; + }; + ++struct passwd; ++_hidden int userlookup_helper_getpwnam(libxl__gc*, const char *user, ++ struct passwd *res, ++ struct passwd **out); ++_hidden int userlookup_helper_getpwuid(libxl__gc*, uid_t uid, ++ struct passwd *res, ++ struct passwd **out); ++ + #endif + + /* +-- +2.42.0 + diff --git a/xsa443-4.16-11.patch b/xsa443-4.16-11.patch new file mode 100644 index 0000000..b3d066b --- /dev/null +++ b/xsa443-4.16-11.patch @@ -0,0 +1,157 @@ +From 273cc7ecf0a66334f24f6f740bcd441b542b3323 Mon Sep 17 00:00:00 2001 +From: Roger Pau Monne +Date: Thu, 28 Sep 2023 12:22:35 +0200 +Subject: [PATCH 11/11] libxl: limit bootloader execution in restricted mode +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Introduce a timeout for bootloader execution when running in restricted mode. + +Allow overwriting the default time out with an environment provided value. + +This is part of XSA-443 / CVE-2023-34325 + +Signed-off-by: Roger Pau Monné +Reviewed-by: Anthony PERARD +--- + docs/man/xl.1.pod.in | 8 ++++++ + tools/libs/light/libxl_bootloader.c | 40 +++++++++++++++++++++++++++++ + tools/libs/light/libxl_internal.h | 2 ++ + 3 files changed, 50 insertions(+) + +diff --git a/docs/man/xl.1.pod.in b/docs/man/xl.1.pod.in +index 96e6fb1c32a3..8f056450a730 100644 +--- a/docs/man/xl.1.pod.in ++++ b/docs/man/xl.1.pod.in +@@ -2007,6 +2007,14 @@ NOTE: Each domain MUST have a SEPARATE username. + + See docs/features/qemu-deprivilege.pandoc for more information. + ++=item LIBXL_BOOTLOADER_TIMEOUT ++ ++Timeout in seconds for bootloader execution when running in restricted mode. ++Otherwise the build time default in LIBXL_BOOTLOADER_TIMEOUT will be used. ++ ++If defined the value must be an unsigned integer between 0 and INT_MAX, ++otherwise behavior is undefined. Setting to 0 disables the timeout. ++ + =back + + =head1 SEE ALSO +diff --git a/tools/libs/light/libxl_bootloader.c b/tools/libs/light/libxl_bootloader.c +index d3a8a4a9ba59..a4beff42654c 100644 +--- a/tools/libs/light/libxl_bootloader.c ++++ b/tools/libs/light/libxl_bootloader.c +@@ -30,6 +30,8 @@ static void bootloader_keystrokes_copyfail(libxl__egc *egc, + libxl__datacopier_state *dc, int rc, int onwrite, int errnoval); + static void bootloader_display_copyfail(libxl__egc *egc, + libxl__datacopier_state *dc, int rc, int onwrite, int errnoval); ++static void bootloader_timeout(libxl__egc *egc, libxl__ev_time *ev, ++ const struct timeval *requested_abs, int rc); + static void bootloader_domaindeath(libxl__egc*, libxl__domaindeathcheck *dc, + int rc); + static void bootloader_finished(libxl__egc *egc, libxl__ev_child *child, +@@ -297,6 +299,7 @@ void libxl__bootloader_init(libxl__bootloader_state *bl) + bl->ptys[0].master = bl->ptys[0].slave = 0; + bl->ptys[1].master = bl->ptys[1].slave = 0; + libxl__ev_child_init(&bl->child); ++ libxl__ev_time_init(&bl->time); + libxl__domaindeathcheck_init(&bl->deathcheck); + bl->keystrokes.ao = bl->ao; libxl__datacopier_init(&bl->keystrokes); + bl->display.ao = bl->ao; libxl__datacopier_init(&bl->display); +@@ -314,6 +317,7 @@ static void bootloader_cleanup(libxl__egc *egc, libxl__bootloader_state *bl) + libxl__domaindeathcheck_stop(gc,&bl->deathcheck); + libxl__datacopier_kill(&bl->keystrokes); + libxl__datacopier_kill(&bl->display); ++ libxl__ev_time_deregister(gc, &bl->time); + for (i=0; i<2; i++) { + libxl__carefd_close(bl->ptys[i].master); + libxl__carefd_close(bl->ptys[i].slave); +@@ -375,6 +379,7 @@ static void bootloader_stop(libxl__egc *egc, + + libxl__datacopier_kill(&bl->keystrokes); + libxl__datacopier_kill(&bl->display); ++ libxl__ev_time_deregister(gc, &bl->time); + if (libxl__ev_child_inuse(&bl->child)) { + r = kill(bl->child.pid, SIGTERM); + if (r) LOGED(WARN, bl->domid, "%sfailed to kill bootloader [%lu]", +@@ -637,6 +642,25 @@ static void bootloader_gotptys(libxl__egc *egc, libxl__openpty_state *op) + + struct termios termattr; + ++ if (getenv("LIBXL_BOOTLOADER_RESTRICT") || ++ getenv("LIBXL_BOOTLOADER_USER")) { ++ const char *timeout_env = getenv("LIBXL_BOOTLOADER_TIMEOUT"); ++ int timeout = timeout_env ? atoi(timeout_env) ++ : LIBXL_BOOTLOADER_TIMEOUT; ++ ++ if (timeout) { ++ /* Set execution timeout */ ++ rc = libxl__ev_time_register_rel(ao, &bl->time, ++ bootloader_timeout, ++ timeout * 1000); ++ if (rc) { ++ LOGED(ERROR, bl->domid, ++ "unable to register timeout for bootloader execution"); ++ goto out; ++ } ++ } ++ } ++ + pid_t pid = libxl__ev_child_fork(gc, &bl->child, bootloader_finished); + if (pid == -1) { + rc = ERROR_FAIL; +@@ -702,6 +726,21 @@ static void bootloader_display_copyfail(libxl__egc *egc, + libxl__bootloader_state *bl = CONTAINER_OF(dc, *bl, display); + bootloader_copyfail(egc, "bootloader output", bl, 1, rc,onwrite,errnoval); + } ++static void bootloader_timeout(libxl__egc *egc, libxl__ev_time *ev, ++ const struct timeval *requested_abs, int rc) ++{ ++ libxl__bootloader_state *bl = CONTAINER_OF(ev, *bl, time); ++ STATE_AO_GC(bl->ao); ++ ++ libxl__ev_time_deregister(gc, &bl->time); ++ ++ assert(libxl__ev_child_inuse(&bl->child)); ++ LOGD(ERROR, bl->domid, "killing bootloader because of timeout"); ++ ++ libxl__ev_child_kill_deregister(ao, &bl->child, SIGKILL); ++ ++ bootloader_callback(egc, bl, rc); ++} + + static void bootloader_domaindeath(libxl__egc *egc, + libxl__domaindeathcheck *dc, +@@ -718,6 +757,7 @@ static void bootloader_finished(libxl__egc *egc, libxl__ev_child *child, + STATE_AO_GC(bl->ao); + int rc; + ++ libxl__ev_time_deregister(gc, &bl->time); + libxl__datacopier_kill(&bl->keystrokes); + libxl__datacopier_kill(&bl->display); + +diff --git a/tools/libs/light/libxl_internal.h b/tools/libs/light/libxl_internal.h +index 8415d1feed16..a9581289f462 100644 +--- a/tools/libs/light/libxl_internal.h ++++ b/tools/libs/light/libxl_internal.h +@@ -103,6 +103,7 @@ + #define LIBXL_QMP_CMD_TIMEOUT 10 + #define LIBXL_STUBDOM_START_TIMEOUT 30 + #define LIBXL_QEMU_BODGE_TIMEOUT 2 ++#define LIBXL_BOOTLOADER_TIMEOUT 120 + #define LIBXL_XENCONSOLE_LIMIT 1048576 + #define LIBXL_XENCONSOLE_PROTOCOL "vt100" + #define LIBXL_MAXMEM_CONSTANT 1024 +@@ -3738,6 +3739,7 @@ struct libxl__bootloader_state { + libxl__openpty_state openpty; + libxl__openpty_result ptys[2]; /* [0] is for bootloader */ + libxl__ev_child child; ++ libxl__ev_time time; + libxl__domaindeathcheck deathcheck; + int nargs, argsspace; + const char **args; +-- +2.42.0 + diff --git a/xsa444-4.16-1.patch b/xsa444-4.16-1.patch new file mode 100644 index 0000000..ca20f96 --- /dev/null +++ b/xsa444-4.16-1.patch @@ -0,0 +1,93 @@ +From: Andrew Cooper +Subject: x86/svm: Fix asymmetry with AMD DR MASK context switching + +The handling of MSR_DR{0..3}_MASK is asymmetric between PV and HVM guests. + +HVM guests context switch in based on the guest view of DBEXT, whereas PV +guest switch in base on the host capability. Both guest types leave the +context dirty for the next vCPU. + +This leads to the following issue: + + * PV or HVM guest has debugging active (%dr7 + mask) + * Switch-out deactivates %dr7 but leaves other state stale in hardware + * Another HVM guest with masks unavailable has debugging active + * Switch in loads %dr7 but leaves the mask MSRs alone + +Now, the second guest's vCPU is operating in the context of the prior vCPU's +mask MSR, while the environment the vCPU can see says there are no mask MSRs. + +As a stopgap, adjust the HVM path to switch in the masks based on host +capabilities rather than guest visibility (i.e. like the PV path). Adjustment +of the intercepts still needs to be dependent on the guest visibility of +DBEXT. + +This is part of XSA-444 / CVE-2023-34327 + +Fixes: c097f54912d3 ("x86/SVM: support data breakpoint extension registers") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c +index a019d196e071..ba4069f9100a 100644 +--- a/xen/arch/x86/hvm/svm/svm.c ++++ b/xen/arch/x86/hvm/svm/svm.c +@@ -185,6 +185,10 @@ static void svm_save_dr(struct vcpu *v) + v->arch.hvm.flag_dr_dirty = 0; + vmcb_set_dr_intercepts(vmcb, ~0u); + ++ /* ++ * The guest can only have changed the mask MSRs if we previous dropped ++ * intercepts. Re-read them from hardware. ++ */ + if ( v->domain->arch.cpuid->extd.dbext ) + { + svm_intercept_msr(v, MSR_AMD64_DR0_ADDRESS_MASK, MSR_INTERCEPT_RW); +@@ -216,17 +220,25 @@ static void __restore_debug_registers(struct vmcb_struct *vmcb, struct vcpu *v) + + ASSERT(v == current); + +- if ( v->domain->arch.cpuid->extd.dbext ) ++ /* ++ * Both the PV and HVM paths leave stale DR_MASK values in hardware on ++ * context-switch-out. If we're activating %dr7 for the guest, we must ++ * sync the DR_MASKs too, whether or not the guest can see them. ++ */ ++ if ( boot_cpu_has(X86_FEATURE_DBEXT) ) + { +- svm_intercept_msr(v, MSR_AMD64_DR0_ADDRESS_MASK, MSR_INTERCEPT_NONE); +- svm_intercept_msr(v, MSR_AMD64_DR1_ADDRESS_MASK, MSR_INTERCEPT_NONE); +- svm_intercept_msr(v, MSR_AMD64_DR2_ADDRESS_MASK, MSR_INTERCEPT_NONE); +- svm_intercept_msr(v, MSR_AMD64_DR3_ADDRESS_MASK, MSR_INTERCEPT_NONE); +- + wrmsrl(MSR_AMD64_DR0_ADDRESS_MASK, v->arch.msrs->dr_mask[0]); + wrmsrl(MSR_AMD64_DR1_ADDRESS_MASK, v->arch.msrs->dr_mask[1]); + wrmsrl(MSR_AMD64_DR2_ADDRESS_MASK, v->arch.msrs->dr_mask[2]); + wrmsrl(MSR_AMD64_DR3_ADDRESS_MASK, v->arch.msrs->dr_mask[3]); ++ ++ if ( v->domain->arch.cpuid->extd.dbext ) ++ { ++ svm_intercept_msr(v, MSR_AMD64_DR0_ADDRESS_MASK, MSR_INTERCEPT_NONE); ++ svm_intercept_msr(v, MSR_AMD64_DR1_ADDRESS_MASK, MSR_INTERCEPT_NONE); ++ svm_intercept_msr(v, MSR_AMD64_DR2_ADDRESS_MASK, MSR_INTERCEPT_NONE); ++ svm_intercept_msr(v, MSR_AMD64_DR3_ADDRESS_MASK, MSR_INTERCEPT_NONE); ++ } + } + + write_debugreg(0, v->arch.dr[0]); +diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c +index f7992ff230b5..a142a63dd869 100644 +--- a/xen/arch/x86/traps.c ++++ b/xen/arch/x86/traps.c +@@ -2314,6 +2314,11 @@ void activate_debugregs(const struct vcpu *curr) + if ( curr->arch.dr7 & DR7_ACTIVE_MASK ) + write_debugreg(7, curr->arch.dr7); + ++ /* ++ * Both the PV and HVM paths leave stale DR_MASK values in hardware on ++ * context-switch-out. If we're activating %dr7 for the guest, we must ++ * sync the DR_MASKs too, whether or not the guest can see them. ++ */ + if ( boot_cpu_has(X86_FEATURE_DBEXT) ) + { + wrmsrl(MSR_AMD64_DR0_ADDRESS_MASK, curr->arch.msrs->dr_mask[0]); diff --git a/xsa444-4.16-2.patch b/xsa444-4.16-2.patch new file mode 100644 index 0000000..b296edf --- /dev/null +++ b/xsa444-4.16-2.patch @@ -0,0 +1,72 @@ +From: Andrew Cooper +Subject: x86/pv: Correct the auditing of guest breakpoint addresses + +The use of access_ok() is buggy, because it permits access to the compat +translation area. 64bit PV guests don't use the XLAT area, but on AMD +hardware, the DBEXT feature allows a breakpoint to match up to a 4G aligned +region, allowing the breakpoint to reach outside of the XLAT area. + +Prior to c/s cda16c1bb223 ("x86: mirror compat argument translation area for +32-bit PV"), the live GDT was within 4G of the XLAT area. + +All together, this allowed a malicious 64bit PV guest on AMD hardware to place +a breakpoint over the live GDT, and trigger a #DB livelock (CVE-2015-8104). + +Introduce breakpoint_addr_ok() and explain why __addr_ok() happens to be an +appropriate check in this case. + +For Xen 4.14 and later, this is a latent bug because the XLAT area has moved +to be on its own with nothing interesting adjacent. For Xen 4.13 and older on +AMD hardware, this fixes a PV-trigger-able DoS. + +This is part of XSA-444 / CVE-2023-34328. + +Fixes: 65e355490817 ("x86/PV: support data breakpoint extension registers") +Signed-off-by: Andrew Cooper +Reviewed-by: Roger Pau Monné +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/pv/misc-hypercalls.c b/xen/arch/x86/pv/misc-hypercalls.c +index 5dade2472687..681c16108fd1 100644 +--- a/xen/arch/x86/pv/misc-hypercalls.c ++++ b/xen/arch/x86/pv/misc-hypercalls.c +@@ -68,7 +68,7 @@ long set_debugreg(struct vcpu *v, unsigned int reg, unsigned long value) + switch ( reg ) + { + case 0 ... 3: +- if ( !access_ok(value, sizeof(long)) ) ++ if ( !breakpoint_addr_ok(value) ) + return -EPERM; + + v->arch.dr[reg] = value; +diff --git a/xen/include/asm-x86/debugreg.h b/xen/include/asm-x86/debugreg.h +index c57914efc6e8..cc298265244b 100644 +--- a/xen/include/asm-x86/debugreg.h ++++ b/xen/include/asm-x86/debugreg.h +@@ -77,6 +77,26 @@ + asm volatile ( "mov %%db" #reg ",%0" : "=r" (__val) ); \ + __val; \ + }) ++ ++/* ++ * Architecturally, %dr{0..3} can have any arbitrary value. However, Xen ++ * can't allow the guest to breakpoint the Xen address range, so we limit the ++ * guest to the lower canonical half, or above the Xen range in the higher ++ * canonical half. ++ * ++ * Breakpoint lengths are specified to mask the low order address bits, ++ * meaning all breakpoints are naturally aligned. With %dr7, the widest ++ * breakpoint is 8 bytes. With DBEXT, the widest breakpoint is 4G. Both of ++ * the Xen boundaries have >4G alignment. ++ * ++ * In principle we should account for HYPERVISOR_COMPAT_VIRT_START(d), but ++ * 64bit Xen has never enforced this for compat guests, and there's no problem ++ * (to Xen) if the guest breakpoints it's alias of the M2P. Skipping this ++ * aspect simplifies the logic, and causes us not to reject a migrating guest ++ * which operated fine on prior versions of Xen. ++ */ ++#define breakpoint_addr_ok(a) __addr_ok(a) ++ + long set_debugreg(struct vcpu *, unsigned int reg, unsigned long value); + void activate_debugregs(const struct vcpu *); + From 5813d5853aa3e2e616b53a60472ee6125b000589 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 15 Nov 2023 20:40:53 +0000 Subject: [PATCH 12/12] 2 security issues x86/AMD: mismatch in IOMMU quarantine page table levels [XSA-445, CVE-2023-46835] x86: BTC/SRSO fixes not fully effective [XSA-446, CVE-2023-46836] --- xen.spec | 11 ++++- xsa445-4.16.patch | 65 ++++++++++++++++++++++++++ xsa446.patch | 115 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 xsa445-4.16.patch create mode 100644 xsa446.patch diff --git a/xen.spec b/xen.spec index ee3bae8..456aba3 100644 --- a/xen.spec +++ b/xen.spec @@ -55,7 +55,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.16.5 -Release: 3%{?dist} +Release: 4%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -138,6 +138,8 @@ Patch70: xsa443-4.16-10.patch Patch71: xsa443-4.16-11.patch Patch72: xsa444-4.16-1.patch Patch73: xsa444-4.16-2.patch +Patch74: xsa445-4.16.patch +Patch75: xsa446.patch %if %build_qemutrad @@ -377,6 +379,8 @@ manage Xen virtual machines. %patch71 -p1 %patch72 -p1 %patch73 -p1 +%patch74 -p1 +%patch75 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -985,6 +989,11 @@ fi %endif %changelog +* Wed Nov 15 2023 Michael Young - 4.16.5-4 +- x86/AMD: mismatch in IOMMU quarantine page table levels [XSA-445, + CVE-2023-46835] +- x86: BTC/SRSO fixes not fully effective [XSA-446, CVE-2023-46836] + * Wed Oct 11 2023 Michael Young - 4.16.5-3 - xenstored: A transaction conflict can crash C Xenstored [XSA-440, CVE-2023-34323] diff --git a/xsa445-4.16.patch b/xsa445-4.16.patch new file mode 100644 index 0000000..5b41c3c --- /dev/null +++ b/xsa445-4.16.patch @@ -0,0 +1,65 @@ +From 88fa5b0db062a8f2ccac4ba05ef75768b2b03e5a Mon Sep 17 00:00:00 2001 +From: Roger Pau Monne +Date: Wed, 11 Oct 2023 13:14:21 +0200 +Subject: [PATCH] iommu/amd-vi: use correct level for quarantine domain page + tables +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The current setup of the quarantine page tables assumes that the quarantine +domain (dom_io) has been initialized with an address width of +DEFAULT_DOMAIN_ADDRESS_WIDTH (48). + +However dom_io being a PV domain gets the AMD-Vi IOMMU page tables levels based +on the maximum (hot pluggable) RAM address, and hence on systems with no RAM +above the 512GB mark only 3 page-table levels are configured in the IOMMU. + +On systems without RAM above the 512GB boundary amd_iommu_quarantine_init() +will setup page tables for the scratch page with 4 levels, while the IOMMU will +be configured to use 3 levels only. The page destined to be used as level 1, +and to contain a directory of PTEs ends up being the address in a PTE itself, +and thus level 1 page becomes the leaf page. Without the level mismatch it's +level 0 page that should be the leaf page instead. + +The level 1 page won't be used as such, and hence it's not possible to use it +to gain access to other memory on the system. However that page is not cleared +in amd_iommu_quarantine_init() as part of re-initialization of the device +quarantine page tables, and hence data on the level 1 page can be leaked +between device usages. + +Fix this by making sure the paging levels setup by amd_iommu_quarantine_init() +match the number configured on the IOMMUs. + +Note that IVMD regions are not affected by this issue, as those areas are +mapped taking the configured paging levels into account. + +This is XSA-445 / CVE-2023-46835 + +Fixes: ea38867831da ('x86 / iommu: set up a scratch page in the quarantine domain') +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +--- + xen/drivers/passthrough/amd/iommu_map.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/xen/drivers/passthrough/amd/iommu_map.c b/xen/drivers/passthrough/amd/iommu_map.c +index cf6f01b633e4..1b414a413b89 100644 +--- a/xen/drivers/passthrough/amd/iommu_map.c ++++ b/xen/drivers/passthrough/amd/iommu_map.c +@@ -654,9 +654,7 @@ static int fill_qpt(union amd_iommu_pte *this, unsigned int level, + int amd_iommu_quarantine_init(struct pci_dev *pdev, bool scratch_page) + { + struct domain_iommu *hd = dom_iommu(dom_io); +- unsigned long end_gfn = +- 1ul << (DEFAULT_DOMAIN_ADDRESS_WIDTH - PAGE_SHIFT); +- unsigned int level = amd_iommu_get_paging_mode(end_gfn); ++ unsigned int level = hd->arch.amd.paging_mode; + unsigned int req_id = get_dma_requestor_id(pdev->seg, pdev->sbdf.bdf); + const struct ivrs_mappings *ivrs_mappings = get_ivrs_mappings(pdev->seg); + int rc; + +base-commit: 29efce0f8f10e381417a61f2f9988b40d4f6bcf0 +-- +2.30.2 + diff --git a/xsa446.patch b/xsa446.patch new file mode 100644 index 0000000..acf1d0f --- /dev/null +++ b/xsa446.patch @@ -0,0 +1,115 @@ +From 80d5aada598c3a800a350003d5d582931545e13c Mon Sep 17 00:00:00 2001 +From: Andrew Cooper +Date: Thu, 26 Oct 2023 14:37:38 +0100 +Subject: [PATCH] x86/spec-ctrl: Remove conditional IRQs-on-ness for INT + $0x80/0x82 paths +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Before speculation defences, some paths in Xen could genuinely get away with +being IRQs-on at entry. But XPTI invalidated this property on most paths, and +attempting to maintain it on the remaining paths was a mistake. + +Fast forward, and DO_SPEC_CTRL_COND_IBPB (protection for AMD BTC/SRSO) is not +IRQ-safe, running with IRQs enabled in some cases. The other actions taken on +these paths happen to be IRQ-safe. + +Make entry_int82() and int80_direct_trap() unconditionally Interrupt Gates +rather than Trap Gates. Remove the conditional re-adjustment of +int80_direct_trap() in smp_prepare_cpus(), and have entry_int82() explicitly +enable interrupts when safe to do so. + +In smp_prepare_cpus(), with the conditional re-adjustment removed, the +clearing of pv_cr3 is the only remaining action gated on XPTI, and it is out +of place anyway, repeating work already done by smp_prepare_boot_cpu(). Drop +the entire if() condition to avoid leaving an incorrect vestigial remnant. + +Also drop comments which make incorrect statements about when its safe to +enable interrupts. + +This is XSA-446 / CVE-2023-46836 + +Signed-off-by: Andrew Cooper +Reviewed-by: Roger Pau Monné +--- + xen/arch/x86/pv/traps.c | 4 ++-- + xen/arch/x86/smpboot.c | 14 -------------- + xen/arch/x86/x86_64/compat/entry.S | 2 ++ + xen/arch/x86/x86_64/entry.S | 1 - + 4 files changed, 4 insertions(+), 17 deletions(-) + +diff --git a/xen/arch/x86/pv/traps.c b/xen/arch/x86/pv/traps.c +index 74f333da7e1c..240d1a2db7a3 100644 +--- a/xen/arch/x86/pv/traps.c ++++ b/xen/arch/x86/pv/traps.c +@@ -139,11 +139,11 @@ void __init pv_trap_init(void) + #ifdef CONFIG_PV32 + /* The 32-on-64 hypercall vector is only accessible from ring 1. */ + _set_gate(idt_table + HYPERCALL_VECTOR, +- SYS_DESC_trap_gate, 1, entry_int82); ++ SYS_DESC_irq_gate, 1, entry_int82); + #endif + + /* Fast trap for int80 (faster than taking the #GP-fixup path). */ +- _set_gate(idt_table + LEGACY_SYSCALL_VECTOR, SYS_DESC_trap_gate, 3, ++ _set_gate(idt_table + LEGACY_SYSCALL_VECTOR, SYS_DESC_irq_gate, 3, + &int80_direct_trap); + + open_softirq(NMI_SOFTIRQ, nmi_softirq); +diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c +index 3a1a659082c6..4c54ecbc91d7 100644 +--- a/xen/arch/x86/smpboot.c ++++ b/xen/arch/x86/smpboot.c +@@ -1158,20 +1158,6 @@ void __init smp_prepare_cpus(void) + + stack_base[0] = (void *)((unsigned long)stack_start & ~(STACK_SIZE - 1)); + +- if ( opt_xpti_hwdom || opt_xpti_domu ) +- { +- get_cpu_info()->pv_cr3 = 0; +- +-#ifdef CONFIG_PV +- /* +- * All entry points which may need to switch page tables have to start +- * with interrupts off. Re-write what pv_trap_init() has put there. +- */ +- _set_gate(idt_table + LEGACY_SYSCALL_VECTOR, SYS_DESC_irq_gate, 3, +- &int80_direct_trap); +-#endif +- } +- + set_nr_sockets(); + + socket_cpumask = xzalloc_array(cpumask_t *, nr_sockets); +diff --git a/xen/arch/x86/x86_64/compat/entry.S b/xen/arch/x86/x86_64/compat/entry.S +index bd5abd8040bd..fcc3a721f147 100644 +--- a/xen/arch/x86/x86_64/compat/entry.S ++++ b/xen/arch/x86/x86_64/compat/entry.S +@@ -21,6 +21,8 @@ ENTRY(entry_int82) + SPEC_CTRL_ENTRY_FROM_PV /* Req: %rsp=regs/cpuinfo, %rdx=0, Clob: acd */ + /* WARNING! `ret`, `call *`, `jmp *` not safe before this point. */ + ++ sti ++ + CR4_PV32_RESTORE + + GET_CURRENT(bx) +diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S +index 5ca74f5f62b2..9a7b129aa7e4 100644 +--- a/xen/arch/x86/x86_64/entry.S ++++ b/xen/arch/x86/x86_64/entry.S +@@ -327,7 +327,6 @@ ENTRY(sysenter_entry) + #ifdef CONFIG_XEN_SHSTK + ALTERNATIVE "", "setssbsy", X86_FEATURE_XEN_SHSTK + #endif +- /* sti could live here when we don't switch page tables below. */ + pushq $FLAT_USER_SS + pushq $0 + pushfq + +base-commit: 7befef87cc9b1bb8ca15d866ce1ecd9165ccb58c +prerequisite-patch-id: 142a87c707411d49e136c3fb76f1b14963ec6dc8 +-- +2.30.2 +