Repository: cupy/cupy

Issue:
No validation for kernel name
 Although `ElementwiseKernel`'s `name` argument is directly used as a function name in generated CUDA code, there are no validation process to raise an exception when invalid characters are used in `name`.
That causes CUDA compile error, which is a bit difficult to debug.


Discussion:

Resolving patch:
diff --git a/cupy/cuda/compiler.py b/cupy/cuda/compiler.py
--- a/cupy/cuda/compiler.py
+++ b/cupy/cuda/compiler.py
@@ -1,6 +1,7 @@
 import hashlib
 import math
 import os
+import re
 import shutil
 import sys
 import tempfile
@@ -227,3 +228,7 @@ def compile(self, options=()):
         except nvrtc.NVRTCError:
             log = nvrtc.getProgramLog(self.ptr)
             raise CompileException(log, self.src, self.name, options)
+
+
+def is_valid_kernel_name(name):
+    return re.match('^[a-zA-Z_][a-zA-Z_0-9]*$', name) is not None


Test patch:
diff --git a/tests/cupy_tests/core_tests/test_elementwise.py b/tests/cupy_tests/core_tests/test_elementwise.py
--- a/tests/cupy_tests/core_tests/test_elementwise.py
+++ b/tests/cupy_tests/core_tests/test_elementwise.py
@@ -1,6 +1,7 @@
 import unittest
 
 import numpy
+import six
 
 import cupy
 from cupy import core
@@ -59,3 +60,13 @@ def test_copy_orders(self, order):
         b_cpu = numpy.copy(a_cpu, order)
 
         self.assertEqual(b.strides, b_cpu.strides)
+
+
+@testing.gpu
+class TestElementwiseInvalidArgument(unittest.TestCase):
+
+    _multiprocess_can_split_ = True
+
+    def test_invalid_kernel_name(self):
+        with six.assertRaisesRegex(self, ValueError, 'Invalid kernel name'):
+            cupy.ElementwiseKernel('T x', '', '', '1')
diff --git a/tests/cupy_tests/core_tests/test_reduction.py b/tests/cupy_tests/core_tests/test_reduction.py
--- a/tests/cupy_tests/core_tests/test_reduction.py
+++ b/tests/cupy_tests/core_tests/test_reduction.py
@@ -98,3 +98,14 @@ def test_shape4(self):
         self.check_int8_sum((512, 256 * 256), axis=1)
         self.check_int8_sum((512 + 1, 256 * 256 + 1), axis=0)
         self.check_int8_sum((512 + 1, 256 * 256 + 1), axis=1)
+
+
+@testing.gpu
+class TestReductionKernelInvalidArgument(unittest.TestCase):
+
+    _multiprocess_can_split_ = True
+
+    def test_invalid_kernel_name(self):
+        with six.assertRaisesRegex(self, ValueError, 'Invalid kernel name'):
+            core.ReductionKernel(
+                'T x', 'T y', 'x', 'a + b', 'y = a', '0', name='1')
diff --git a/tests/cupy_tests/cuda_tests/test_compiler.py b/tests/cupy_tests/cuda_tests/test_compiler.py
--- a/tests/cupy_tests/cuda_tests/test_compiler.py
+++ b/tests/cupy_tests/cuda_tests/test_compiler.py
@@ -13,3 +13,24 @@ def test(self):
         # An error message contains the file name `kern.cu`
         with six.assertRaisesRegex(self, compiler.CompileException, 'kern.cu'):
             compiler.compile_using_nvrtc('a')
+
+
+class TestIsValidKernelName(unittest.TestCase):
+
+    def test_valid(self):
+        self.assertTrue(compiler.is_valid_kernel_name('valid_name_1'))
+
+    def test_empyt(self):
+        self.assertFalse(compiler.is_valid_kernel_name(''))
+
+    def test_start_with_digit(self):
+        self.assertFalse(compiler.is_valid_kernel_name('0_invalid'))
+
+    def test_new_line(self):
+        self.assertFalse(compiler.is_valid_kernel_name('invalid\nname'))
+
+    def test_symbol(self):
+        self.assertFalse(compiler.is_valid_kernel_name('invalid$name'))
+
+    def test_space(self):
+        self.assertFalse(compiler.is_valid_kernel_name('invalid name'))