Repository: nodejs/node
Pull request: win,build: add option to enable cfg

This adds an option to `vcbuild.bat` to compile with CFG (Control Flow Guard). 

CFG is a programming or security concept used to ensure that the execution of a program follows its intended flow and prevents unauthorized or unintended behavior. It is often used to detect and mitigate attacks that exploit vulnerabilities, such as control flow hijacking, which occurs when an attacker manipulates the program’s control flow to execute malicious code.

- CFG validates that indirect calls (e.g., calls via function pointers) and jumps go to legitimate, pre-determined destinations within the program.
- When the application is compiled with CFG enabled, the compiler generates metadata about the valid control flow targets.
- At runtime, Windows uses this metadata to verify the destination of indirect calls or jumps before allowing them.

More information about the CFG can be found at https://learn.microsoft.com/en-us/windows/win32/secbp/control-flow-guard

Fixes: https://github.com/nodejs/node/issues/42100
<!--
Before submitting a pull request, please read:

- the CONTRIBUTING guide at https://github.com/nodejs/node/blob/HEAD/CONTRIBUTING.md
- the commit message formatting guidelines at
  https://github.com/nodejs/node/blob/HEAD/doc/contributing/pull-requests.md#commit-message-guidelines

For code changes:
1. Include tests for any bug fixes or new features.
2. Update documentation if relevant.
3. Ensure that `make -j4 test` (UNIX), or `vcbuild test` (Windows) passes.

If you believe this PR should be highlighted in the Node.js CHANGELOG
please add the `notable-change` label.

Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.
-->


Changed files:
filename: common.gypi
patch: @@ -28,6 +28,7 @@
     'clang%': 0,
     'error_on_warn%': 'false',
     'suppress_all_error_on_warn%': 'false',
+    'control_flow_guard%': 'false',
 
     'openssl_product': '<(STATIC_LIB_PREFIX)openssl<(STATIC_LIB_SUFFIX)',
     'openssl_no_asm%': 0,
@@ -310,6 +311,11 @@
               '/Zm2000',
             ],
           }],
+          ['control_flow_guard=="true"', {
+            'AdditionalOptions': [
+              '/guard:cf',                        # Control Flow Guard
+            ],
+          }],
         ],
         'BufferSecurityCheck': 'true',
         'DebugInformationFormat': 1,          # /Z7 embed info in .obj files
@@ -336,6 +342,11 @@
           ['target_arch=="arm64"', {
             'TargetMachine' : 0,              # NotSet. MACHINE:ARM64 is inferred from the input files.
           }],
+          ['control_flow_guard=="true"', {
+            'AdditionalOptions': [
+              '/guard:cf',                        # Control Flow Guard
+            ],
+          }],
         ],
         'GenerateDebugInformation': 'true',
         'SuppressStartupBanner': 'true',

filename: configure.py
patch: @@ -819,6 +819,12 @@
     default=None,
     help='do not install the bundled Corepack')
 
+parser.add_argument('--control-flow-guard',
+    action='store_true',
+    dest='enable_cfg',
+    default=None,
+    help='enable Control Flow Guard (CFG)')
+
 # Dummy option for backwards compatibility
 parser.add_argument('--without-report',
     action='store_true',
@@ -1443,6 +1449,7 @@ def configure_node(o):
   o['variables']['node_prefix'] = options.prefix
   o['variables']['node_install_npm'] = b(not options.without_npm)
   o['variables']['node_install_corepack'] = b(not options.without_corepack)
+  o['variables']['control_flow_guard'] = b(options.enable_cfg)
   o['variables']['node_use_amaro'] = b(not options.without_amaro)
   o['variables']['debug_node'] = b(options.debug_node)
   o['default_configuration'] = 'Debug' if options.debug else 'Release'

filename: vcbuild.bat
patch: @@ -72,6 +72,7 @@ set no_shared_roheap=
 set doc=
 set extra_msbuild_args=
 set compile_commands=
+set cfg=
 set exit_code=0
 
 :next-arg
@@ -150,6 +151,7 @@ if /i "%1"=="no-shared-roheap" set no_shared_roheap=1&goto arg-ok
 if /i "%1"=="doc"           set doc=1&goto arg-ok
 if /i "%1"=="binlog"        set extra_msbuild_args=/binaryLogger:out\%config%\node.binlog&goto arg-ok
 if /i "%1"=="compile-commands" set compile_commands=1&goto arg-ok
+if /i "%1"=="cfg"           set cfg=1&goto arg-ok
 
 echo Error: invalid command line option `%1`.
 exit /b 1
@@ -210,6 +212,7 @@ if defined no_shared_roheap set configure_flags=%configure_flags% --disable-shar
 if defined DEBUG_HELPER     set configure_flags=%configure_flags% --verbose
 if defined ccache_path      set configure_flags=%configure_flags% --use-ccache-win
 if defined compile_commands set configure_flags=%configure_flags% -C
+if defined cfg              set configure_flags=%configure_flags% --control-flow-guard
 
 if "%target_arch%"=="x86" (
   echo "32-bit Windows builds are not supported anymore."