seb-win-refactoring: ERROR: Caught unexpected exception while performing operation 'VirtualMachineOperation'

IMPORTANT Please always consult the documentation first before creating a bug report! https://safeexambrowser.org/windows/win_usermanual_en.html

Describe the Bug When I try to execute SEB in Windows 10, the app does not start correctly, older versions work fine, but latest 3.6.0 not.

Steps to Reproduce Steps to reproduce the behavior:

  1. Go to Moodle and start a new quiz attempt
  2. The SEB will crash

Expected Behavior To start the quiz attempt with out any problems

Screenshots Screenshot 2023-12-08 at 23 03 47

Version Information

  • Windows 10 Professional
  • SEB-Version 3.6.0

Additional Context

2023-12-06 21:09:10.587 [09] - INFO: Validating virtual machine policy...
2023-12-06 21:09:10.603 [09] - ERROR: Caught unexpected exception while performing operation 'VirtualMachineOperation'!

   Exception Message: Object reference not set to an instance of an object.
   Exception Type: System.NullReferenceException

   at SafeExamBrowser.SystemComponents.VirtualMachineDetector.IsVirtualSystem(String biosInfo, String manufacturer, String model) in C:\Users\appveyor\projects\seb-win-refactoring-phv3mbelx16fm9ou\SafeExamBrowser.SystemComponents\VirtualMachineDetector.cs:line 116
   at SafeExamBrowser.SystemComponents.VirtualMachineDetector.HasHistoricVirtualMachineHardwareConfiguration() in C:\Users\appveyor\projects\seb-win-refactoring-phv3mbelx16fm9ou\SafeExamBrowser.SystemComponents\VirtualMachineDetector.cs:line 158
   at SafeExamBrowser.SystemComponents.VirtualMachineDetector.IsVirtualMachine() in C:\Users\appveyor\projects\seb-win-refactoring-phv3mbelx16fm9ou\SafeExamBrowser.SystemComponents\VirtualMachineDetector.cs:line 60
   at SafeExamBrowser.Runtime.Operations.VirtualMachineOperation.ValidatePolicy() in C:\Users\appveyor\projects\seb-win-refactoring-phv3mbelx16fm9ou\SafeExamBrowser.Runtime\Operations\VirtualMachineOperation.cs:line 54
   at SafeExamBrowser.Core.OperationModel.OperationSequence.Perform() in C:\Users\appveyor\projects\seb-win-refactoring-phv3mbelx16fm9ou\SafeExamBrowser.Core\OperationModel\OperationSequence.cs:line 108

2023-12-06 21:09:10.606 [09] - INFO: ### -------------------------------------- Session Start Failed -------------------------------------- ###

2023-12-06 21:11:21.342 [09] - INFO: Initiating shutdown procedure...
2023-12-06 21:11:21.344 [09] - INFO: Stopping communication host...

About this issue

  • Original URL
  • State: open
  • Created 7 months ago
  • Comments: 15 (10 by maintainers)

Most upvoted comments

Yes indeed, I think there is no way around actually trying out different approaches. I think value != default should work, as value is defined as an object reference and not a value type like e.g. int, but here again, I think the best would be to try it out and see what works (best). I shall take this up in our internal planning and tackle it next year.

@Notselwyn Yes, I think you’re right, that is actually a bug in the code which we should fix. The current implementation will indeed return true with an output value of null if the name and/or the key do not exist. Ideally, we should thus first check whether the key and name do in fact exist and only then attempt to read the value, but I could not find an API that would allow to do so. Are you aware of one?

That is indeed the best approach. I haven’t looked into the semantics but perhaps open a registry key, use RegistryKey.GetValueNames(), validate the value name, and then get the value? The obvious problem with this is that there’s quite a bit of overhead, so the approach depends on how frequent the TryRead() function is called in the codebase.

Would you agree that the above change in fact eliminates the bug and ensures that if we return true, then the output value will never be null? Or can there in fact be registry names with a value of null, which in turn would prevent us from checking for value != default?

I believe this would interfere when registry values overlap with default values for types. I.e. if a registry value type is int and the registry value is 0, then the return value may indicate failure considering registry value 0 is the default value. If we take this approach I recommend using the fact that an existing registry value should not be NULL:

public bool TryRead(string key, string name, out object value)
{
+	var raw_value = NULL;  // set to NULL for fallback when exception happens

	try
	{
+		raw_value = Microsoft.Win32.Registry.GetValue(key, name, null);
	}
	catch (Exception e)
	{
		logger.Error($"Failed to read value '{name}' from registry key '{key}'!", e);
	}
	
+	if (raw_value == NULL)
+       {   
+           value = default;
+           return false;
+       }

+       value = raw_value;
+       return true;
}

(I’m using a separate var here which could be NULL, so the value out arg will never be set to NULL considering I don’t know out of the top of my head if it can be )