Skip to content

Commit 9ea53a0

Browse files
author
Gang Ji
committed
CA-382035: Verify commandline items for service process in "pid"
When xenopsd-xc getting process id of a service, verify that its commandline items include expected process name and some commandline arg containing domid if domid is not part of its process name. Signed-off-by: Gang Ji <gang.ji@citrix.com>
1 parent 01dfc98 commit 9ea53a0

File tree

3 files changed

+93
-52
lines changed

3 files changed

+93
-52
lines changed

ocaml/xapi/xapi.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -924,7 +924,7 @@ let report_tls_verification ~__context =
924924

925925
let server_init () =
926926
let print_server_starting_message () =
927-
debug "(Re)starting xapi" ;
927+
debug "(Re)starting xapi, pid: %d" (Unix.getpid ()) ;
928928
debug "on_system_boot=%b pool_role=%s" !Xapi_globs.on_system_boot
929929
(Pool_role.string_of (Pool_role.get_role ()))
930930
in

ocaml/xenopsd/lib/xenopsd.ml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,8 @@ let doc =
404404
let configure ?(specific_options = []) ?(specific_essential_paths = [])
405405
?(specific_nonessential_paths = []) () =
406406
Debug.set_facility Syslog.Local5 ;
407-
debug "xenopsd version %s starting" Xapi_version.version ;
407+
debug "xenopsd version %s starting, pid: %d" Xapi_version.version
408+
(Unix.getpid ()) ;
408409
let options = options @ specific_options in
409410
let resources =
410411
Resources.make_resources

ocaml/xenopsd/xc/service.ml

Lines changed: 90 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,15 @@ module type DAEMONPIDPATH = sig
235235
val name : string
236236

237237
val pid_location : Pid.path
238+
239+
(* To check if a pid is valid, look up its process name, and some commandline
240+
arg containing domid if domid is not part of its process name, check that
241+
they are contained in /proc/<pid>/cmdline.
242+
The expected cmdline items are set by (expected_cmdline_items domid) for
243+
each service. Note that the parameter "domid" here is required as
244+
otherwise we can not distinguish between the process instances of the same
245+
service for different domains. *)
246+
val expected_cmdline_items : domid:int -> string list
238247
end
239248

240249
module DaemonMgmt (D : DAEMONPIDPATH) = struct
@@ -256,31 +265,66 @@ module DaemonMgmt (D : DAEMONPIDPATH) = struct
256265

257266
let name = D.name
258267

259-
let pid ~xs domid =
268+
(* For process id, look up its process name, and some commandline arg
269+
containing domid if domid is not part of its process name, check that
270+
they are contained in /proc/<pid>/cmdline. *)
271+
let is_cmdline_valid ~pid ~pid_source expected_args =
260272
try
261-
match D.pid_location with
262-
| (File file | Path_of {file; _}) when Sys.file_exists (file domid) ->
263-
let path = file domid in
264-
let ( let* ) = Option.bind in
265-
let* pid = Unixext.pidfile_read path in
266-
Unixext.with_file path [Unix.O_RDONLY] 0 (fun fd ->
267-
try
268-
Unix.lockf fd Unix.F_TRLOCK 0 ;
269-
(* we succeeded taking the lock: original process is dead.
270-
* some other process might've reused its pid *)
271-
None
272-
with Unix.Unix_error (Unix.EAGAIN, _, _) ->
273-
(* cannot obtain lock: process is alive *)
274-
Some pid
275-
)
276-
| Xenstore key | Path_of {key; _} ->
277-
(* backward compatibility during update installation: only has
278-
xenstore pid available *)
279-
let pid = xs.Xs.read (key domid) in
280-
Some (int_of_string pid)
273+
let cmdline_str =
274+
Printf.sprintf "/proc/%d/cmdline" pid |> Unixext.string_of_file
275+
in
276+
match cmdline_str with
277+
| "" ->
278+
(* from man proc:
279+
/proc/[pid]/cmdline
280+
This read-only file holds the complete command line for the process,
281+
unless the process is a zombie. In the latter case, there is nothing
282+
in this file: that is, a read on this file will return 0 characters.
283+
This applies when the VM is being shut down. *)
284+
false
281285
| _ ->
282-
None
283-
with _ -> None
286+
let cmdline = Astring.String.cuts ~sep:"\000" cmdline_str in
287+
let valid =
288+
List.for_all (fun arg -> List.mem arg cmdline) expected_args
289+
in
290+
if not valid then
291+
error "%s: pid read from %s not valid (pid = %d)" D.name pid_source
292+
pid ;
293+
valid
294+
with _ -> false
295+
296+
let pid ~xs domid =
297+
let ( let* ) = Option.bind in
298+
let* pid, pid_source =
299+
try
300+
match D.pid_location with
301+
| (File file | Path_of {file; _}) when Sys.file_exists (file domid) ->
302+
let path = file domid in
303+
let* pid = Unixext.pidfile_read path in
304+
Unixext.with_file path [Unix.O_RDONLY] 0 (fun fd ->
305+
try
306+
Unix.lockf fd Unix.F_TRLOCK 0 ;
307+
(* we succeeded taking the lock: original process is dead.
308+
* some other process might've reused its pid *)
309+
None
310+
with Unix.Unix_error (Unix.EAGAIN, _, _) ->
311+
(* cannot obtain lock: process is alive *)
312+
Some (pid, "pidfile")
313+
)
314+
| Xenstore key | Path_of {key; _} ->
315+
(* case 1: backward compatibility during update installation: only has
316+
xenstore pid available.
317+
case 2: pidfile(file domid) got removed unexpectly *)
318+
let pid = xs.Xs.read (key domid) in
319+
Some (int_of_string pid, "xenstore")
320+
| _ ->
321+
None
322+
with _ -> None
323+
in
324+
if is_cmdline_valid ~pid ~pid_source (D.expected_cmdline_items ~domid) then
325+
Some pid
326+
else
327+
None
284328

285329
let is_running ~xs domid =
286330
match pid ~xs domid with
@@ -362,6 +406,8 @@ module Qemu = struct
362406
let name = name
363407

364408
let pid_location = Pid.Path_of {key= pidxenstore_path; file= pidfile_path}
409+
410+
let expected_cmdline_items ~domid = [Printf.sprintf "qemu-dm-%d" domid]
365411
end)
366412

367413
module SignalMask = D.SignalMask
@@ -404,12 +450,16 @@ module Qemu = struct
404450
end
405451

406452
module Vgpu = struct
453+
let domain_arg domid = Printf.sprintf "--domain=%d" domid
454+
407455
module D = DaemonMgmt (struct
408456
let name = "vgpu"
409457

410458
let pid_path domid = Printf.sprintf "/local/domain/%d/vgpu-pid" domid
411459

412460
let pid_location = Pid.Xenstore pid_path
461+
462+
let expected_cmdline_items ~domid = [!Xc_resources.vgpu; domain_arg domid]
413463
end)
414464

415465
(** An NVidia Virtual Compute Service vGPU has a class attribute
@@ -507,7 +557,7 @@ module Vgpu = struct
507557
let suspend_file = Printf.sprintf Device_common.demu_save_path domid in
508558
let base_args =
509559
[
510-
"--domain=" ^ string_of_int domid
560+
domain_arg domid
511561
; "--vcpus=" ^ string_of_int vcpus
512562
; "--suspend=" ^ suspend_file
513563
]
@@ -612,6 +662,9 @@ module Varstored = struct
612662
let name = name
613663

614664
let pid_location = Pid.Path_of {key= pidxenstore_path; file= pidfile_path}
665+
666+
let expected_cmdline_items ~domid =
667+
[!Xc_resources.varstored; pidfile_path domid]
615668
end)
616669

617670
let efivars_resume_path =
@@ -686,6 +739,8 @@ module Swtpm = struct
686739
let name = "swtpm"
687740

688741
let pid_location = Pid.File pidfile_path
742+
743+
let expected_cmdline_items ~domid = [Printf.sprintf "swtpm-%d" domid]
689744
end)
690745

691746
let xs_path ~domid = Device_common.get_private_path domid ^ "/vtpm"
@@ -809,13 +864,19 @@ module PV_Vnc = struct
809864
let pidxenstore_path domid =
810865
Printf.sprintf "/local/domain/%d/vncterm-pid" domid
811866

867+
let vnc_console_path domid = Printf.sprintf "/local/domain/%d/console" domid
868+
812869
module D = DaemonMgmt (struct
813870
let name = "vncterm"
814871

815872
let pid_location = Pid.Xenstore pidxenstore_path
816-
end)
817873

818-
let vnc_console_path domid = Printf.sprintf "/local/domain/%d/console" domid
874+
let expected_cmdline_items ~domid =
875+
[
876+
!Xc_resources.vncterm (* vncterm binary path *)
877+
; vnc_console_path domid (* xenstore console path *)
878+
]
879+
end)
819880

820881
let vnc_port_path domid =
821882
Printf.sprintf "/local/domain/%d/console/vnc-port" domid
@@ -825,36 +886,15 @@ module PV_Vnc = struct
825886

826887
let pid ~xs domid = D.pid ~xs domid
827888

828-
(* Look up the commandline args for the vncterm pid; *)
829-
(* Check that they include the vncterm binary path and the xenstore console
830-
path for the supplied domid. *)
831-
let is_cmdline_valid domid pid =
832-
try
833-
let cmdline =
834-
Printf.sprintf "/proc/%d/cmdline" pid
835-
|> Unixext.string_of_file
836-
|> Astring.String.cuts ~sep:"\000"
837-
in
838-
List.mem !Xc_resources.vncterm cmdline
839-
&& List.mem (vnc_console_path domid) cmdline
840-
with _ -> false
841-
842-
let is_vncterm_running ~xs domid =
843-
match pid ~xs domid with
844-
| None ->
845-
false
846-
| Some p ->
847-
D.is_running ~xs domid && is_cmdline_valid domid p
848-
849889
let get_vnc_port ~xs domid =
850-
if not (is_vncterm_running ~xs domid) then
890+
if not (D.is_running ~xs domid) then
851891
None
852892
else
853893
try Some (Socket.Port (int_of_string (xs.Xs.read (vnc_port_path domid))))
854894
with _ -> None
855895

856896
let get_tc_port ~xs domid =
857-
if not (is_vncterm_running ~xs domid) then
897+
if not (D.is_running ~xs domid) then
858898
None
859899
else
860900
try Some (int_of_string (xs.Xs.read (tc_port_path domid)))
@@ -913,7 +953,7 @@ module PV_Vnc = struct
913953
let l =
914954
[
915955
"-x"
916-
; Printf.sprintf "/local/domain/%d/console" domid
956+
; vnc_console_path domid
917957
; "-T"
918958
; (* listen for raw connections *)
919959
"-v"

0 commit comments

Comments
 (0)