mesa/main: validate name syntax for array variables only
[mesa.git] / src / mesa / main / program_resource.c
1 /*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 2015 Intel Corporation. All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 *
24 */
25
26 #include "main/enums.h"
27 #include "main/macros.h"
28 #include "main/mtypes.h"
29 #include "main/shaderapi.h"
30 #include "main/shaderobj.h"
31 #include "program_resource.h"
32
33 static bool
34 supported_interface_enum(GLenum iface)
35 {
36 switch (iface) {
37 case GL_UNIFORM:
38 case GL_UNIFORM_BLOCK:
39 case GL_PROGRAM_INPUT:
40 case GL_PROGRAM_OUTPUT:
41 case GL_TRANSFORM_FEEDBACK_VARYING:
42 case GL_ATOMIC_COUNTER_BUFFER:
43 return true;
44 case GL_VERTEX_SUBROUTINE:
45 case GL_TESS_CONTROL_SUBROUTINE:
46 case GL_TESS_EVALUATION_SUBROUTINE:
47 case GL_GEOMETRY_SUBROUTINE:
48 case GL_FRAGMENT_SUBROUTINE:
49 case GL_COMPUTE_SUBROUTINE:
50 case GL_VERTEX_SUBROUTINE_UNIFORM:
51 case GL_TESS_CONTROL_SUBROUTINE_UNIFORM:
52 case GL_TESS_EVALUATION_SUBROUTINE_UNIFORM:
53 case GL_GEOMETRY_SUBROUTINE_UNIFORM:
54 case GL_FRAGMENT_SUBROUTINE_UNIFORM:
55 case GL_COMPUTE_SUBROUTINE_UNIFORM:
56 case GL_BUFFER_VARIABLE:
57 case GL_SHADER_STORAGE_BLOCK:
58 default:
59 return false;
60 }
61 }
62
63 void GLAPIENTRY
64 _mesa_GetProgramInterfaceiv(GLuint program, GLenum programInterface,
65 GLenum pname, GLint *params)
66 {
67 GET_CURRENT_CONTEXT(ctx);
68 unsigned i;
69 struct gl_shader_program *shProg =
70 _mesa_lookup_shader_program_err(ctx, program,
71 "glGetProgramInterfaceiv");
72 if (!shProg)
73 return;
74
75 if (!params) {
76 _mesa_error(ctx, GL_INVALID_OPERATION,
77 "glGetProgramInterfaceiv(params NULL)");
78 return;
79 }
80
81 /* Validate interface. */
82 if (!supported_interface_enum(programInterface)) {
83 _mesa_error(ctx, GL_INVALID_OPERATION, "glGetProgramInterfaceiv(%s)",
84 _mesa_lookup_enum_by_nr(programInterface));
85 return;
86 }
87
88 /* Validate pname against interface. */
89 switch(pname) {
90 case GL_ACTIVE_RESOURCES:
91 for (i = 0, *params = 0; i < shProg->NumProgramResourceList; i++)
92 if (shProg->ProgramResourceList[i].Type == programInterface)
93 (*params)++;
94 break;
95 case GL_MAX_NAME_LENGTH:
96 if (programInterface == GL_ATOMIC_COUNTER_BUFFER) {
97 _mesa_error(ctx, GL_INVALID_OPERATION,
98 "glGetProgramInterfaceiv(%s pname %s)",
99 _mesa_lookup_enum_by_nr(programInterface),
100 _mesa_lookup_enum_by_nr(pname));
101 return;
102 }
103 /* Name length consists of base name, 3 additional chars '[0]' if
104 * resource is an array and finally 1 char for string terminator.
105 */
106 for (i = 0, *params = 0; i < shProg->NumProgramResourceList; i++) {
107 if (shProg->ProgramResourceList[i].Type != programInterface)
108 continue;
109 const char *name =
110 _mesa_program_resource_name(&shProg->ProgramResourceList[i]);
111 unsigned array_size =
112 _mesa_program_resource_array_size(&shProg->ProgramResourceList[i]);
113 *params = MAX2(*params, strlen(name) + (array_size ? 3 : 0) + 1);
114 }
115 break;
116 case GL_MAX_NUM_ACTIVE_VARIABLES:
117 switch (programInterface) {
118 case GL_UNIFORM_BLOCK:
119 for (i = 0, *params = 0; i < shProg->NumProgramResourceList; i++) {
120 if (shProg->ProgramResourceList[i].Type == programInterface) {
121 struct gl_uniform_block *block =
122 (struct gl_uniform_block *)
123 shProg->ProgramResourceList[i].Data;
124 *params = MAX2(*params, block->NumUniforms);
125 }
126 }
127 break;
128 case GL_ATOMIC_COUNTER_BUFFER:
129 for (i = 0, *params = 0; i < shProg->NumProgramResourceList; i++) {
130 if (shProg->ProgramResourceList[i].Type == programInterface) {
131 struct gl_active_atomic_buffer *buffer =
132 (struct gl_active_atomic_buffer *)
133 shProg->ProgramResourceList[i].Data;
134 *params = MAX2(*params, buffer->NumUniforms);
135 }
136 }
137 break;
138 default:
139 _mesa_error(ctx, GL_INVALID_OPERATION,
140 "glGetProgramInterfaceiv(%s pname %s)",
141 _mesa_lookup_enum_by_nr(programInterface),
142 _mesa_lookup_enum_by_nr(pname));
143 };
144 break;
145 case GL_MAX_NUM_COMPATIBLE_SUBROUTINES:
146 default:
147 _mesa_error(ctx, GL_INVALID_OPERATION,
148 "glGetProgramInterfaceiv(pname %s)",
149 _mesa_lookup_enum_by_nr(pname));
150 }
151 }
152
153 static bool
154 is_xfb_marker(const char *str)
155 {
156 static const char *markers[] = {
157 "gl_NextBuffer",
158 "gl_SkipComponents1",
159 "gl_SkipComponents2",
160 "gl_SkipComponents3",
161 "gl_SkipComponents4",
162 NULL
163 };
164 const char **m = markers;
165
166 if (strncmp(str, "gl_", 3) != 0)
167 return false;
168
169 for (; *m; m++)
170 if (strcmp(*m, str) == 0)
171 return true;
172
173 return false;
174 }
175
176 /**
177 * Checks if given name index is legal for GetProgramResourceIndex,
178 * check is written to be compatible with GL_ARB_array_of_arrays.
179 */
180 static bool
181 valid_program_resource_index_name(const GLchar *name)
182 {
183 const char *array = strstr(name, "[");
184 const char *close = strrchr(name, ']');
185
186 /* Not array, no need for the check. */
187 if (!array)
188 return true;
189
190 /* Last array index has to be zero. */
191 if (!close || *--close != '0')
192 return false;
193
194 return true;
195 }
196
197 GLuint GLAPIENTRY
198 _mesa_GetProgramResourceIndex(GLuint program, GLenum programInterface,
199 const GLchar *name)
200 {
201 GET_CURRENT_CONTEXT(ctx);
202 struct gl_program_resource *res;
203 struct gl_shader_program *shProg =
204 _mesa_lookup_shader_program_err(ctx, program,
205 "glGetProgramResourceIndex");
206 if (!shProg || !name)
207 return GL_INVALID_INDEX;
208
209 /*
210 * For the interface TRANSFORM_FEEDBACK_VARYING, the value INVALID_INDEX
211 * should be returned when querying the index assigned to the special names
212 * "gl_NextBuffer", "gl_SkipComponents1", "gl_SkipComponents2",
213 * "gl_SkipComponents3", and "gl_SkipComponents4".
214 */
215 if (programInterface == GL_TRANSFORM_FEEDBACK_VARYING &&
216 is_xfb_marker(name))
217 return GL_INVALID_INDEX;
218
219 switch (programInterface) {
220 case GL_PROGRAM_INPUT:
221 case GL_PROGRAM_OUTPUT:
222 case GL_UNIFORM:
223 case GL_TRANSFORM_FEEDBACK_VARYING:
224 /* Validate name syntax for array variables */
225 if (!valid_program_resource_index_name(name))
226 return GL_INVALID_INDEX;
227 /* fall-through */
228 case GL_UNIFORM_BLOCK:
229 res = _mesa_program_resource_find_name(shProg, programInterface, name);
230 if (!res)
231 return GL_INVALID_INDEX;
232
233 return _mesa_program_resource_index(shProg, res);
234 case GL_ATOMIC_COUNTER_BUFFER:
235 default:
236 _mesa_error(ctx, GL_INVALID_ENUM, "glGetProgramResourceIndex(%s)",
237 _mesa_lookup_enum_by_nr(programInterface));
238 }
239
240 return GL_INVALID_INDEX;
241 }
242
243 void GLAPIENTRY
244 _mesa_GetProgramResourceName(GLuint program, GLenum programInterface,
245 GLuint index, GLsizei bufSize, GLsizei *length,
246 GLchar *name)
247 {
248 GET_CURRENT_CONTEXT(ctx);
249 struct gl_shader_program *shProg =
250 _mesa_lookup_shader_program_err(ctx, program,
251 "glGetProgramResourceName");
252
253 /* Set user friendly return values in case of errors. */
254 if (name)
255 *name = '\0';
256 if (length)
257 *length = 0;
258
259 if (!shProg || !name)
260 return;
261
262 if (programInterface == GL_ATOMIC_COUNTER_BUFFER ||
263 !supported_interface_enum(programInterface)) {
264 _mesa_error(ctx, GL_INVALID_ENUM, "glGetProgramResourceName(%s)",
265 _mesa_lookup_enum_by_nr(programInterface));
266 return;
267 }
268
269 _mesa_get_program_resource_name(shProg, programInterface, index, bufSize,
270 length, name, "glGetProgramResourceName");
271 }
272
273 void GLAPIENTRY
274 _mesa_GetProgramResourceiv(GLuint program, GLenum programInterface,
275 GLuint index, GLsizei propCount,
276 const GLenum *props, GLsizei bufSize,
277 GLsizei *length, GLint *params)
278 {
279 GET_CURRENT_CONTEXT(ctx);
280 struct gl_shader_program *shProg =
281 _mesa_lookup_shader_program_err(ctx, program, "glGetProgramResourceiv");
282
283 if (!shProg || !params)
284 return;
285
286 /* The error INVALID_VALUE is generated if <propCount> is zero.
287 * Note that we check < 0 here because it makes sense to bail early.
288 */
289 if (propCount <= 0) {
290 _mesa_error(ctx, GL_INVALID_VALUE,
291 "glGetProgramResourceiv(propCount <= 0)");
292 return;
293 }
294
295 /* No need to write any properties, user requested none. */
296 if (bufSize == 0)
297 return;
298
299 _mesa_get_program_resourceiv(shProg, programInterface, index,
300 propCount, props, bufSize, length, params);
301 }
302
303 /**
304 * Function verifies syntax of given name for GetProgramResourceLocation
305 * and GetProgramResourceLocationIndex for the following cases:
306 *
307 * "array element portion of a string passed to GetProgramResourceLocation
308 * or GetProgramResourceLocationIndex must not have, a "+" sign, extra
309 * leading zeroes, or whitespace".
310 *
311 * Check is written to be compatible with GL_ARB_array_of_arrays.
312 */
313 static bool
314 invalid_array_element_syntax(const GLchar *name)
315 {
316 char *first = strchr(name, '[');
317 char *last = strrchr(name, '[');
318
319 if (!first)
320 return false;
321
322 /* No '+' or ' ' allowed anywhere. */
323 if (strchr(first, '+') || strchr(first, ' '))
324 return true;
325
326 /* Check that last array index is 0. */
327 if (last[1] == '0' && last[2] != ']')
328 return true;
329
330 return false;
331 }
332
333 static struct gl_shader_program *
334 lookup_linked_program(GLuint program, const char *caller)
335 {
336 GET_CURRENT_CONTEXT(ctx);
337 struct gl_shader_program *prog =
338 _mesa_lookup_shader_program_err(ctx, program, caller);
339
340 if (!prog)
341 return NULL;
342
343 if (prog->LinkStatus == GL_FALSE) {
344 _mesa_error(ctx, GL_INVALID_OPERATION, "%s(program not linked)",
345 caller);
346 return NULL;
347 }
348 return prog;
349 }
350
351 GLint GLAPIENTRY
352 _mesa_GetProgramResourceLocation(GLuint program, GLenum programInterface,
353 const GLchar *name)
354 {
355 GET_CURRENT_CONTEXT(ctx);
356 struct gl_shader_program *shProg =
357 lookup_linked_program(program, "glGetProgramResourceLocation");
358
359 if (!shProg || !name || invalid_array_element_syntax(name))
360 return -1;
361
362 /* Validate programInterface. */
363 switch (programInterface) {
364 case GL_UNIFORM:
365 case GL_PROGRAM_INPUT:
366 case GL_PROGRAM_OUTPUT:
367 break;
368
369 /* For reference valid cases requiring additional extension support:
370 * GL_ARB_shader_subroutine
371 * GL_ARB_tessellation_shader
372 * GL_ARB_compute_shader
373 */
374 case GL_VERTEX_SUBROUTINE_UNIFORM:
375 case GL_TESS_CONTROL_SUBROUTINE_UNIFORM:
376 case GL_TESS_EVALUATION_SUBROUTINE_UNIFORM:
377 case GL_GEOMETRY_SUBROUTINE_UNIFORM:
378 case GL_FRAGMENT_SUBROUTINE_UNIFORM:
379 case GL_COMPUTE_SUBROUTINE_UNIFORM:
380
381 default:
382 _mesa_error(ctx, GL_INVALID_ENUM, "glGetProgramResourceLocation(%s %s)",
383 _mesa_lookup_enum_by_nr(programInterface), name);
384 }
385
386 return _mesa_program_resource_location(shProg, programInterface, name);
387 }
388
389 /**
390 * Returns output index for dual source blending.
391 */
392 GLint GLAPIENTRY
393 _mesa_GetProgramResourceLocationIndex(GLuint program, GLenum programInterface,
394 const GLchar *name)
395 {
396 GET_CURRENT_CONTEXT(ctx);
397 struct gl_shader_program *shProg =
398 lookup_linked_program(program, "glGetProgramResourceLocationIndex");
399
400 if (!shProg || !name || invalid_array_element_syntax(name))
401 return -1;
402
403 /* From the GL_ARB_program_interface_query spec:
404 *
405 * "For GetProgramResourceLocationIndex, <programInterface> must be
406 * PROGRAM_OUTPUT."
407 */
408 if (programInterface != GL_PROGRAM_OUTPUT) {
409 _mesa_error(ctx, GL_INVALID_ENUM,
410 "glGetProgramResourceLocationIndex(%s)",
411 _mesa_lookup_enum_by_nr(programInterface));
412 return -1;
413 }
414
415 return _mesa_program_resource_location_index(shProg, GL_PROGRAM_OUTPUT,
416 name);
417 }