Merge branch 'some-bounds-checks' into 'master'
Add some bounds checking to _reply functions See merge request xorg/lib/libxcb!29
This commit is contained in:
commit
0ff8c8c824
21
configure.ac
21
configure.ac
|
@ -15,7 +15,7 @@ AM_INIT_AUTOMAKE([foreign dist-xz])
|
|||
AM_PATH_PYTHON([3.0])
|
||||
|
||||
# Set common system defines for POSIX extensions, such as _GNU_SOURCE
|
||||
# Must be called before any macros that run the compiler (like AC_PROG_LIBTOOL)
|
||||
# Must be called before any macros that run the compiler (like LT_INIT)
|
||||
# to avoid autoconf errors.
|
||||
AC_USE_SYSTEM_EXTENSIONS
|
||||
AC_SYS_LARGEFILE
|
||||
|
@ -27,6 +27,8 @@ LT_INIT([win32-dll])
|
|||
# Require xorg-macros minimum of 1.18 - Initial version
|
||||
m4_ifndef([XORG_MACROS_VERSION],
|
||||
[m4_fatal([must install xorg-macros 1.18 or later before running autoconf/autogen])])
|
||||
XORG_TESTSET_CFLAG([XCB_CFLAGS], [-fno-strict-aliasing])
|
||||
AC_SUBST([XCB_CFLAGS])
|
||||
XORG_MACROS_VERSION(1.18)
|
||||
XORG_DEFAULT_OPTIONS
|
||||
XORG_ENABLE_DEVEL_DOCS
|
||||
|
@ -248,6 +250,23 @@ XCB_EXTENSION(XTest, yes)
|
|||
XCB_EXTENSION(Xv, yes)
|
||||
XCB_EXTENSION(XvMC, yes)
|
||||
|
||||
AC_MSG_CHECKING([[if __attribute__((may_alias)) is supported]])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||
typedef struct x { int a; } __attribute__((may_alias)) x;
|
||||
]])],[
|
||||
AC_DEFINE([[XCB_MAY_ALIAS]], [[__attribute__((may_alias))]], [Define if __attribute__((may_alias)) works])
|
||||
AC_MSG_RESULT([[yes]])
|
||||
],[
|
||||
AC_MSG_RESULT([[no]])
|
||||
AC_MSG_CHECKING([[if -fno-strict-aliasing works]])[
|
||||
case $XCB_CFLAGS in
|
||||
*-fno-strict-aliasing*)]
|
||||
AC_MSG_RESULT([[yes]])
|
||||
AC_MSG_WARN([[may_alias not supported, programs must use -fno-strict-aliasing]]);;
|
||||
*)
|
||||
AC_MSG_RESULT([[no (flags are $XCB_CFLAGS)]])
|
||||
AC_MSG_FAILURE([[No method of suppressing strict-aliasing found]]);;
|
||||
[esac]])
|
||||
AC_ARG_WITH(serverside-support, AS_HELP_STRING([--with-serverside-support], [Build with support for server-side usage of xcb. This is still EXPERIMENTAL! ABI/API may change! (default: no)]), [XCB_SERVERSIDE_SUPPORT=$withval], [XCB_SERVERSIDE_SUPPORT=no])
|
||||
|
||||
AM_CONDITIONAL(XCB_SERVERSIDE_SUPPORT, test "x$XCB_SERVERSIDE_SUPPORT" = "xyes")
|
||||
|
|
|
@ -4,7 +4,7 @@ EXTSOURCES = xproto.c \
|
|||
bigreq.c \
|
||||
xc_misc.c
|
||||
|
||||
AM_CFLAGS = $(BASE_CFLAGS) $(NEEDED_CFLAGS) $(XDMCP_CFLAGS)
|
||||
AM_CFLAGS = $(BASE_CFLAGS) $(NEEDED_CFLAGS) $(XDMCP_CFLAGS) $(XCB_CFLAGS)
|
||||
libxcb_la_LIBADD = $(NEEDED_LIBS) $(XDMCP_LIBS)
|
||||
libxcb_la_SOURCES = \
|
||||
xcb_conn.c xcb_out.c xcb_in.c xcb_ext.c xcb_xid.c \
|
||||
|
@ -249,7 +249,7 @@ nodist_libxcb_ge_la_SOURCES = ge.c ge.h
|
|||
endif
|
||||
|
||||
EXTHEADERS=$(EXTSOURCES:.c=.h)
|
||||
xcbinclude_HEADERS = xcb.h xcbext.h
|
||||
xcbinclude_HEADERS = xcb.h xcbext.h config.h
|
||||
if XCB_HAVE_WIN32
|
||||
xcbinclude_HEADERS += xcb_windefs.h
|
||||
endif
|
||||
|
|
309
src/c_client.py
309
src/c_client.py
|
@ -113,6 +113,7 @@ class PreCode(object):
|
|||
|
||||
|
||||
def output_tempvars(self):
|
||||
assert False
|
||||
if self.redirect_code == None:
|
||||
_c_wr_stringlist('', self.tempvars)
|
||||
self.tempvars = []
|
||||
|
@ -278,9 +279,10 @@ def c_open(self):
|
|||
_h('#include "xcb.h"')
|
||||
|
||||
_c('#ifdef HAVE_CONFIG_H')
|
||||
_c('#include "config.h"')
|
||||
_hc('#include "config.h"')
|
||||
_c('#endif')
|
||||
_c('#include <stdlib.h>')
|
||||
_c('#include <stdalign.h>')
|
||||
_c('#include <string.h>')
|
||||
_c('#include <assert.h>')
|
||||
_c('#include <stddef.h> /* for offsetof() */')
|
||||
|
@ -288,7 +290,6 @@ def c_open(self):
|
|||
_c('#include "%s.h"', _ns.header)
|
||||
|
||||
_c('')
|
||||
_c('#define ALIGNOF(type) offsetof(struct { char dummy; type member; }, member)')
|
||||
|
||||
if _ns.is_ext:
|
||||
for (n, h) in self.direct_imports:
|
||||
|
@ -308,6 +309,13 @@ def c_open(self):
|
|||
|
||||
_c('')
|
||||
_c('xcb_extension_t %s = { "%s", 0 };', _ns.c_ext_global_name, _ns.ext_xname)
|
||||
_c('static int xcb_checked_mul(unsigned long long x, unsigned long long y)')
|
||||
_c('{')
|
||||
_c(' int z;')
|
||||
_c(' if (__builtin_mul_overflow(x, y, &z))')
|
||||
_c(' return -1;')
|
||||
_c(' return z;')
|
||||
_c('}')
|
||||
|
||||
def c_close(self):
|
||||
'''
|
||||
|
@ -528,12 +536,14 @@ def _c_type_setup(self, name, postfix):
|
|||
# special cases -> unserialize
|
||||
if self.is_switch or self.c_var_followed_by_fixed_fields:
|
||||
_c_serialize('unserialize', self)
|
||||
_c_serialize('unserialize_checked', self)
|
||||
|
||||
if self.c_need_sizeof:
|
||||
if self.c_sizeof_name not in finished_sizeof:
|
||||
if not module.namespace.is_ext or self.name[:2] == module.namespace.prefix:
|
||||
finished_sizeof.append(self.c_sizeof_name)
|
||||
_c_serialize('sizeof', self)
|
||||
_c_serialize('sizeof_checked', self)
|
||||
|
||||
# Functions for querying field properties
|
||||
def _c_field_needs_list_accessor(field):
|
||||
|
@ -791,8 +801,12 @@ def get_serialize_params(context, self, buffer_var='_buffer', aux_var='_aux'):
|
|||
# 1. the parameter for the void * buffer
|
||||
if 'serialize' == context:
|
||||
params.append(('void', '**', buffer_var))
|
||||
elif context in ('unserialize', 'unpack', 'sizeof'):
|
||||
elif context in ('unserialize', 'unserialize_checked', 'unpack', 'unpack_checked', 'sizeof', 'sizeof_checked'):
|
||||
params.append(('const void', '*', buffer_var))
|
||||
if context.endswith('checked'):
|
||||
params.append(('const void', '*', 'xcb_end'))
|
||||
else:
|
||||
assert False
|
||||
|
||||
# 2. any expr fields that cannot be resolved within self and descendants
|
||||
unresolved_fields = resolve_expr_fields(self)
|
||||
|
@ -815,9 +829,9 @@ def get_serialize_params(context, self, buffer_var='_buffer', aux_var='_aux'):
|
|||
# 4. aux argument
|
||||
if 'serialize' == context:
|
||||
add_param(params, ('const %s' % self.c_type, '*', aux_var))
|
||||
elif 'unserialize' == context:
|
||||
elif context.startswith('unserialize'):
|
||||
add_param(params, ('%s' % self.c_type, '**', aux_var))
|
||||
elif 'unpack' == context:
|
||||
elif context.startswith('unpack'):
|
||||
add_param(params, ('%s' % self.c_type, '*', aux_var))
|
||||
|
||||
# 5. switch contains all variable size fields as struct members
|
||||
|
@ -849,7 +863,9 @@ def _c_serialize_helper_insert_padding(context, complex_type, code_lines, space,
|
|||
code_lines.append('%s xcb_parts[xcb_parts_idx].iov_base = xcb_pad0;' % space)
|
||||
code_lines.append('%s xcb_parts[xcb_parts_idx].iov_len = xcb_pad;' % space)
|
||||
code_lines.append('%s xcb_parts_idx++;' % space)
|
||||
elif context in ('unserialize', 'unpack', 'sizeof'):
|
||||
elif context in ('unserialize', 'unserialize_checked', 'unpack', 'unpack_checked', 'sizeof', 'sizeof_checked'):
|
||||
if context.endswith('checked'):
|
||||
_c_emit_bounds_check(code_lines, space + ' ', 'xcb_pad')
|
||||
code_lines.append('%s xcb_tmp += xcb_pad;' % space)
|
||||
|
||||
code_lines.append('%s xcb_pad = 0;' % space)
|
||||
|
@ -866,7 +882,7 @@ def _c_serialize_helper_switch(context, self, complex_name,
|
|||
code_lines, temp_vars,
|
||||
space, prefix):
|
||||
count = 0
|
||||
switch_expr = _c_accessor_get_expr(self.expr, None)
|
||||
switch_expr = _c_accessor_get_expr(self.expr, None, checked=context.endswith("checked"))
|
||||
|
||||
for b in self.bitcases:
|
||||
len_expr = len(b.type.expr)
|
||||
|
@ -878,7 +894,7 @@ def _c_serialize_helper_switch(context, self, complex_name,
|
|||
compare_operator = '&'
|
||||
|
||||
for n, expr in enumerate(b.type.expr):
|
||||
bitcase_expr = _c_accessor_get_expr(expr, None)
|
||||
bitcase_expr = _c_accessor_get_expr(expr, None, checked=context.endswith("checked"))
|
||||
# only one <enumref> in the <bitcase>
|
||||
if len_expr == 1:
|
||||
code_lines.append(
|
||||
|
@ -907,7 +923,7 @@ def _c_serialize_helper_switch(context, self, complex_name,
|
|||
|
||||
# if 'serialize' == context:
|
||||
# count += _c_serialize_helper_insert_padding(context, self, code_lines, space, False)
|
||||
# elif context in ('unserialize', 'unpack', 'sizeof'):
|
||||
# elif context in ('unserialize', 'unserialize_checked', 'unpack', 'unpack_checked', 'sizeof', 'sizeof_checked'):
|
||||
# # padding
|
||||
# code_lines.append('%s xcb_pad = -xcb_block_len & 3;' % space)
|
||||
# code_lines.append('%s xcb_buffer_len += xcb_block_len + xcb_pad;' % space)
|
||||
|
@ -946,13 +962,16 @@ def _c_serialize_helper_switch_field(context, self, field, c_switch_variable, pr
|
|||
if 'serialize' == context:
|
||||
length = "%s(&%s, %s&%s%s)" % (field.type.c_serialize_name, c_switch_variable,
|
||||
c_field_names, prefix_str, field.c_field_name)
|
||||
elif context in ('unserialize', 'unpack'):
|
||||
length = "%s(xcb_tmp, %s&%s%s)" % (field.type.c_unpack_name,
|
||||
c_field_names, prefix_str, field.c_field_name)
|
||||
elif 'sizeof' == context:
|
||||
elif context in ('unserialize', 'unserialize_checked', 'unpack', 'unpack_checked'):
|
||||
length = "%s%s(xcb_tmp, %s%s&%s%s)" % (field.type.c_unpack_name, '_checked' if context.endswith('checked') else '',
|
||||
'xcb_end, ' if context.endswith('checked') else '',
|
||||
c_field_names, prefix_str, field.c_field_name)
|
||||
elif context in ('sizeof', 'sizeof_checked'):
|
||||
# remove trailing ", " from c_field_names because it will be used at end of arglist
|
||||
my_c_field_names = c_field_names[:-2]
|
||||
length = "%s(xcb_tmp, %s)" % (field.type.c_sizeof_name, my_c_field_names)
|
||||
else:
|
||||
assert False, 'bad context %r' % context
|
||||
|
||||
return length
|
||||
|
||||
|
@ -1011,10 +1030,10 @@ def _c_serialize_helper_list_field(context, self, field,
|
|||
if expr.op == 'calculate_len':
|
||||
list_length = field.type.expr.lenfield_name
|
||||
else:
|
||||
list_length = _c_accessor_get_expr(expr, field_mapping)
|
||||
list_length = _c_accessor_get_expr(expr, field_mapping, checked=context.endswith("checked"))
|
||||
|
||||
# default: list with fixed size elements
|
||||
length = '%s * sizeof(%s)' % (list_length, field.type.member.c_wiretype)
|
||||
length = 'xcb_checked_mul(%s, sizeof(%s))' % (list_length, field.type.member.c_wiretype)
|
||||
|
||||
# list with variable-sized elements
|
||||
if not field.type.member.fixed_size():
|
||||
|
@ -1027,7 +1046,7 @@ def _c_serialize_helper_list_field(context, self, field,
|
|||
|
||||
#
|
||||
length = ''
|
||||
if context in ('unserialize', 'sizeof', 'unpack'):
|
||||
if context in ('unserialize', 'unserialize_checked', 'sizeof', 'unpack', 'unpack_checked', 'sizeof_checked'):
|
||||
int_i = ' unsigned int i;'
|
||||
xcb_tmp_len = ' unsigned int xcb_tmp_len;'
|
||||
if int_i not in temp_vars:
|
||||
|
@ -1037,8 +1056,18 @@ def _c_serialize_helper_list_field(context, self, field,
|
|||
# loop over all list elements and call sizeof repeatedly
|
||||
# this should be a bit faster than using the iterators
|
||||
code_lines.append("%s for(i=0; i<%s; i++) {" % (space, list_length))
|
||||
code_lines.append("%s xcb_tmp_len = %s(xcb_tmp%s);" %
|
||||
(space, field.type.c_sizeof_name, member_arg_str))
|
||||
if context.endswith('checked'):
|
||||
code_lines.append('%s if ((const char *)xcb_end < (const char *)xcb_tmp)' % (space,))
|
||||
code_lines.append('%s abort();' % (space,))
|
||||
code_lines.append("%s xcb_tmp_len = %s_checked(xcb_tmp, xcb_end%s);" %
|
||||
(space, field.type.c_sizeof_name, member_arg_str))
|
||||
code_lines.append("%s if (xcb_tmp_len < 0)" % (space,))
|
||||
code_lines.append("%s return xcb_tmp_len;" % (space,))
|
||||
code_lines.append('%s if ((size_t)((const char *)xcb_end - (const char *)xcb_tmp) < xcb_tmp_len)' % (space,))
|
||||
code_lines.append('%s return -1;' % (space,))
|
||||
else:
|
||||
code_lines.append("%s xcb_tmp_len = %s(xcb_tmp%s);" %
|
||||
(space, field.type.c_sizeof_name, member_arg_str))
|
||||
code_lines.append("%s xcb_block_len += xcb_tmp_len;" % space)
|
||||
code_lines.append("%s xcb_tmp += xcb_tmp_len;" % space)
|
||||
code_lines.append("%s }" % space)
|
||||
|
@ -1069,7 +1098,7 @@ def _c_serialize_helper_fields_fixed_size(context, self, field,
|
|||
# default for simple cases: call sizeof()
|
||||
length = "sizeof(%s)" % field.c_field_type
|
||||
|
||||
if context in ('unserialize', 'unpack', 'sizeof'):
|
||||
if context in ('unserialize', 'unserialize_checked', 'unpack', 'unpack_checked', 'sizeof', 'sizeof_checked'):
|
||||
# default: simple cast
|
||||
value = ' %s = *(%s *)xcb_tmp;' % (abs_field_name, field.c_field_type)
|
||||
|
||||
|
@ -1077,7 +1106,7 @@ def _c_serialize_helper_fields_fixed_size(context, self, field,
|
|||
if field.type.is_pad and field.type.nmemb > 1:
|
||||
value = ''
|
||||
for i in range(field.type.nmemb):
|
||||
code_lines.append('%s %s[%d] = *(%s *)xcb_tmp;' %
|
||||
code_lines.append('%s %s[%d] = *(const %s *)xcb_tmp;' %
|
||||
(space, abs_field_name, i, field.c_field_type))
|
||||
# total padding = sizeof(pad0) * nmemb
|
||||
length += " * %d" % field.type.nmemb
|
||||
|
@ -1097,10 +1126,10 @@ def _c_serialize_helper_fields_fixed_size(context, self, field,
|
|||
# need to register a temporary variable for the expression in case we know its type
|
||||
if field.type.c_type is None:
|
||||
raise Exception("type for field '%s' (expression '%s') unkown" %
|
||||
(field.field_name, _c_accessor_get_expr(field.type.expr)))
|
||||
(field.field_name, _c_accessor_get_expr(field.type.expr, prefix, False)))
|
||||
|
||||
temp_vars.append(' %s xcb_expr_%s = %s;' % (field.type.c_type, _cpp(field.field_name),
|
||||
_c_accessor_get_expr(field.type.expr, prefix)))
|
||||
_c_accessor_get_expr(field.type.expr, prefix, False)))
|
||||
value += "&xcb_expr_%s;" % _cpp(field.field_name)
|
||||
|
||||
elif field.type.is_pad:
|
||||
|
@ -1128,16 +1157,16 @@ def _c_serialize_helper_fields_variable_size(context, self, field,
|
|||
space, prefix):
|
||||
prefix_str = _c_helper_fieldaccess_expr(prefix)
|
||||
|
||||
if context in ('unserialize', 'unpack', 'sizeof'):
|
||||
if context in ('unserialize', 'unserialize_checked', 'unpack', 'unpack_checked', 'sizeof', 'sizeof_checked'):
|
||||
value = ''
|
||||
var_field_name = 'xcb_tmp'
|
||||
|
||||
# special case: intermixed fixed and variable size fields
|
||||
if self.c_var_followed_by_fixed_fields and 'unserialize' == context:
|
||||
if self.c_var_followed_by_fixed_fields and context.startswith('unserialize'):
|
||||
value = ' %s = (%s *)xcb_tmp;' % (field.c_field_name, field.c_field_type)
|
||||
temp_vars.append(' %s *%s;' % (field.type.c_type, field.c_field_name))
|
||||
# special case: switch
|
||||
if 'unpack' == context:
|
||||
if context.startswith('unpack'):
|
||||
value = ' %s%s = (%s *)xcb_tmp;' % (prefix_str, field.c_field_name, field.c_field_type)
|
||||
|
||||
elif 'serialize' == context:
|
||||
|
@ -1175,6 +1204,12 @@ def _c_serialize_helper_fields_variable_size(context, self, field,
|
|||
|
||||
return (value, length)
|
||||
|
||||
def _c_emit_bounds_check(code_lines, space, length):
|
||||
code_lines.append('%s if ((const char *)xcb_end < (const char *)xcb_tmp)' % (space,))
|
||||
code_lines.append('%s abort();' % (space,))
|
||||
code_lines.append('%s if ((size_t)((const char *)xcb_end - (const char *)xcb_tmp) < %s)' % (space, length))
|
||||
code_lines.append('%s return -1;' % (space,))
|
||||
|
||||
def _c_serialize_helper_fields(context, self,
|
||||
code_lines, temp_vars,
|
||||
space, prefix, is_case_or_bitcase):
|
||||
|
@ -1188,7 +1223,7 @@ def _c_serialize_helper_fields(context, self,
|
|||
if not field.wire:
|
||||
continue
|
||||
if not field.visible:
|
||||
if not ((field.wire and not field.auto) or 'unserialize' == context):
|
||||
if not ((field.wire and not field.auto) or context.startswith('unserialize')):
|
||||
continue
|
||||
|
||||
# switch/bitcase: fixed size fields must be considered explicitly
|
||||
|
@ -1239,24 +1274,39 @@ def _c_serialize_helper_fields(context, self,
|
|||
if field.type.fixed_size():
|
||||
if is_case_or_bitcase or self.c_var_followed_by_fixed_fields:
|
||||
# keep track of (un)serialized object's size
|
||||
code_lines.append('%s xcb_block_len += %s;' % (space, length))
|
||||
assert 'xcb_block_len' not in length
|
||||
if context in ('unserialize', 'unpack', 'sizeof'):
|
||||
code_lines.append('%s xcb_tmp += %s;' % (space, length))
|
||||
elif context in ('unserialize_checked', 'sizeof_checked', 'unpack_checked'):
|
||||
_c_emit_bounds_check(code_lines, space, length)
|
||||
code_lines.append('%s xcb_tmp += %s;' % (space, length))
|
||||
code_lines.append('%s xcb_block_len += %s;' % (space, length))
|
||||
else:
|
||||
# variable size objects or bitcase:
|
||||
# value & length might have been inserted earlier for special cases
|
||||
if '' != length:
|
||||
# special case: intermixed fixed and variable size fields
|
||||
if (not field.type.fixed_size() and
|
||||
self.c_var_followed_by_fixed_fields and 'unserialize' == context):
|
||||
self.c_var_followed_by_fixed_fields and context.startswith('unserialize')):
|
||||
temp_vars.append(' int %s_len;' % field.c_field_name)
|
||||
code_lines.append('%s %s_len = %s;' % (space, field.c_field_name, length))
|
||||
code_lines.append('%s if (__builtin_add_overflow(xcb_block_len, %s_len, &xcb_block_len))' % (space, field.c_field_name))
|
||||
if context.endswith('checked'):
|
||||
code_lines.append('%s return -1;' % (space,))
|
||||
_c_emit_bounds_check(code_lines, space, field.c_field_name)
|
||||
else:
|
||||
code_lines.append('%s abort();' % (space,))
|
||||
code_lines.append('%s xcb_block_len += %s_len;' % (space, field.c_field_name))
|
||||
code_lines.append('%s xcb_tmp += %s_len;' % (space, field.c_field_name))
|
||||
else:
|
||||
code_lines.append('%s xcb_block_len += %s;' % (space, length))
|
||||
code_lines.append('%s if (__builtin_add_overflow(xcb_block_len, %s, &xcb_block_len))' % (space, length))
|
||||
if context.endswith('checked'):
|
||||
code_lines.append('%s return -1;' % (space,))
|
||||
_c_emit_bounds_check(code_lines, space, 'xcb_block_len')
|
||||
else:
|
||||
code_lines.append('%s abort();' % (space,))
|
||||
# increase pointer into the byte stream accordingly
|
||||
if context in ('unserialize', 'sizeof', 'unpack'):
|
||||
if context in ('unserialize', 'unserialize_checked', 'sizeof', 'unpack', 'unpack_checked', 'sizeof_checked'):
|
||||
code_lines.append('%s xcb_tmp += xcb_block_len;' % space)
|
||||
|
||||
if 'serialize' == context:
|
||||
|
@ -1266,7 +1316,7 @@ def _c_serialize_helper_fields(context, self,
|
|||
count += 1
|
||||
|
||||
code_lines.append(
|
||||
'%s xcb_align_to = ALIGNOF(%s);'
|
||||
'%s xcb_align_to = alignof(%s);'
|
||||
% (space,
|
||||
'char'
|
||||
if field.c_field_type == 'void' or field.type.is_switch
|
||||
|
@ -1305,8 +1355,13 @@ def _c_serialize_helper(context, complex_type,
|
|||
# all other data types can be evaluated one field a time
|
||||
else:
|
||||
# unserialize & fixed size fields: simply cast the buffer to the respective xcb_out type
|
||||
if context in ('unserialize', 'unpack', 'sizeof') and not self.c_var_followed_by_fixed_fields:
|
||||
code_lines.append('%s xcb_block_len += sizeof(%s);' % (space, self.c_type))
|
||||
if context != 'serialize' and not self.c_var_followed_by_fixed_fields:
|
||||
code_lines.append('%s if (__builtin_add_overflow(xcb_block_len, sizeof(%s), &xcb_block_len))' % (space, self.c_type))
|
||||
if context.endswith('checked'):
|
||||
code_lines.append('%s return -1;' % (space,))
|
||||
_c_emit_bounds_check(code_lines, space, 'xcb_block_len')
|
||||
else:
|
||||
code_lines.append('%s abort();' % (space,))
|
||||
code_lines.append('%s xcb_tmp += xcb_block_len;' % space)
|
||||
code_lines.append('%s xcb_buffer_len += xcb_block_len;' % space)
|
||||
code_lines.append('%s xcb_block_len = 0;' % space)
|
||||
|
@ -1321,7 +1376,7 @@ def _c_serialize_helper(context, complex_type,
|
|||
|
||||
def _c_serialize(context, self):
|
||||
"""
|
||||
depending on the context variable, generate _serialize(), _unserialize(), _unpack(), or _sizeof()
|
||||
depending on the context variable, generate _serialize(), _unserialize(), _unpack(), _sizeof(), or _sizeof_checked()
|
||||
for the ComplexType variable self
|
||||
"""
|
||||
_h_setlevel(1)
|
||||
|
@ -1331,13 +1386,16 @@ def _c_serialize(context, self):
|
|||
# _serialize() returns the buffer size
|
||||
_hc('int')
|
||||
|
||||
if self.is_switch and 'unserialize' == context:
|
||||
context = 'unpack'
|
||||
if self.is_switch and context.startswith('unserialize'):
|
||||
context = context.replace('serialize', 'pack')
|
||||
|
||||
cases = { 'serialize' : self.c_serialize_name,
|
||||
'unserialize' : self.c_unserialize_name,
|
||||
'unpack' : self.c_unpack_name,
|
||||
'sizeof' : self.c_sizeof_name }
|
||||
cases = { 'serialize' : self.c_serialize_name,
|
||||
'unserialize' : self.c_unserialize_name,
|
||||
'unserialize_checked' : self.c_unserialize_name + '_checked',
|
||||
'unpack' : self.c_unpack_name,
|
||||
'unpack_checked' : self.c_unpack_name + '_checked',
|
||||
'sizeof' : self.c_sizeof_name,
|
||||
'sizeof_checked' : self.c_sizeof_name + '_checked' }
|
||||
func_name = cases[context]
|
||||
|
||||
param_fields, wire_fields, params = get_serialize_params(context, self)
|
||||
|
@ -1391,7 +1449,7 @@ def _c_serialize(context, self):
|
|||
prefix = [('_aux', '->', self)]
|
||||
aux_ptr = 'xcb_out'
|
||||
|
||||
elif context in ('unserialize', 'unpack'):
|
||||
elif context in ('unserialize', 'unserialize_checked', 'unpack', 'unpack_checked'):
|
||||
_c(' char *xcb_tmp = (char *)_buffer;')
|
||||
if not self.is_switch:
|
||||
if not self.c_var_followed_by_fixed_fields:
|
||||
|
@ -1415,22 +1473,27 @@ def _c_serialize(context, self):
|
|||
_c(' unsigned int xcb_padding_offset = %d;',
|
||||
self.get_align_offset() )
|
||||
|
||||
elif 'sizeof' == context:
|
||||
elif context in ('sizeof', 'sizeof_checked'):
|
||||
param_names = [p[2] for p in params]
|
||||
if self.length_expr is not None:
|
||||
_c(' const %s *_aux = (%s *)_buffer;', self.c_type, self.c_type)
|
||||
prefix = [('_aux', '->', self)]
|
||||
|
||||
field_mapping = _c_get_field_mapping_for_expr(self, self.length_expr, prefix)
|
||||
|
||||
_c(' return %s;', _c_accessor_get_expr(self.length_expr, field_mapping))
|
||||
v = _c_accessor_get_expr(self.length_expr, field_mapping, checked=context.endswith('checked'))
|
||||
_c('')
|
||||
for t in temp_vars:
|
||||
_c(t)
|
||||
_c('')
|
||||
for l in code_lines:
|
||||
_c(l)
|
||||
_c(' return %s;', v)
|
||||
_c('}')
|
||||
_c_pre.redirect_end()
|
||||
return
|
||||
if self.is_switch:
|
||||
# switch: call _unpack()
|
||||
_c(' %s _aux;', self.c_type)
|
||||
_c(' return %s(%s, &_aux);', self.c_unpack_name, ", ".join(param_names))
|
||||
_c(' return %s%s(%s, &_aux);', self.c_unpack_name, context[6:], ", ".join(param_names))
|
||||
_c('}')
|
||||
_c_pre.redirect_end()
|
||||
return
|
||||
|
@ -1445,6 +1508,8 @@ def _c_serialize(context, self):
|
|||
prefix = [('_aux', '->', self)]
|
||||
if self.is_switch:
|
||||
_c(' unsigned int xcb_padding_offset = 0;')
|
||||
else:
|
||||
assert False, 'bad context %r' % context
|
||||
|
||||
count = _c_serialize_helper(context, self, code_lines, temp_vars, prefix=prefix)
|
||||
# update variable size fields (only important for context=='serialize'
|
||||
|
@ -1457,7 +1522,7 @@ def _c_serialize(context, self):
|
|||
temp_vars.append(' unsigned int xcb_block_len = 0;')
|
||||
temp_vars.append(' unsigned int i;')
|
||||
temp_vars.append(' char *xcb_tmp;')
|
||||
elif 'sizeof' == context:
|
||||
elif context.startswith('sizeof'):
|
||||
# neither switch nor intermixed fixed and variable size fields:
|
||||
# evaluate parameters directly
|
||||
if not (self.is_switch or self.c_var_followed_by_fixed_fields):
|
||||
|
@ -1486,9 +1551,9 @@ def _c_serialize(context, self):
|
|||
# variable sized fields have been collected, now
|
||||
# allocate memory and copy everything into a continuous memory area
|
||||
# note: this is not necessary in case of unpack
|
||||
if context in ('serialize', 'unserialize'):
|
||||
if context in ('serialize', 'unserialize', 'unserialize_checked'):
|
||||
# unserialize: check for sizeof-only invocation
|
||||
if 'unserialize' == context:
|
||||
if context.startswith('unserialize'):
|
||||
_c('')
|
||||
_c(' if (NULL == _aux)')
|
||||
_c(' return xcb_buffer_len;')
|
||||
|
@ -1497,6 +1562,8 @@ def _c_serialize(context, self):
|
|||
_c(' if (NULL == %s) {', aux_ptr)
|
||||
_c(' /* allocate memory */')
|
||||
_c(' %s = malloc(xcb_buffer_len);', aux_ptr)
|
||||
_c(' if (%s == NULL)', aux_ptr)
|
||||
_c(' return -1;')
|
||||
if 'serialize' == context:
|
||||
_c(' *_buffer = xcb_out;')
|
||||
_c(' }')
|
||||
|
@ -1525,8 +1592,8 @@ def _c_serialize(context, self):
|
|||
_c(' }')
|
||||
|
||||
# unserialize: assign variable size fields individually
|
||||
if 'unserialize' == context:
|
||||
_c(' xcb_tmp = ((char *)*_aux)+xcb_buffer_len;')
|
||||
if context.startswith('unserialize'):
|
||||
_c(' xcb_tmp = ((const char *)*_aux)+xcb_buffer_len;')
|
||||
param_fields.reverse()
|
||||
for field in param_fields:
|
||||
if not field.type.fixed_size():
|
||||
|
@ -1575,7 +1642,7 @@ def _c_iterator(self, name):
|
|||
param[0],
|
||||
' ' * (len(self.c_type) + 1 - len(param[0])),
|
||||
param[2])
|
||||
_h('} %s;', self.c_iterator_type)
|
||||
_h('} XCB_MAY_ALIAS %s;', self.c_iterator_type)
|
||||
|
||||
_h_setlevel(1)
|
||||
_c_setlevel(1)
|
||||
|
@ -1673,7 +1740,7 @@ def _c_accessor_get_length(expr, field_mapping=None):
|
|||
else:
|
||||
return str(expr.nmemb)
|
||||
|
||||
def _c_accessor_get_expr(expr, field_mapping):
|
||||
def _c_accessor_get_expr(expr, field_mapping, *, checked):
|
||||
'''
|
||||
Figures out what C code is needed to get the length of a list field.
|
||||
The field_mapping parameter can be used to change the absolute name of a length field.
|
||||
|
@ -1683,11 +1750,11 @@ def _c_accessor_get_expr(expr, field_mapping):
|
|||
'''
|
||||
lenexp = _c_accessor_get_length(expr, field_mapping)
|
||||
|
||||
if expr.op == '~':
|
||||
return '(' + '~' + _c_accessor_get_expr(expr.rhs, field_mapping) + ')'
|
||||
elif expr.op == 'popcount':
|
||||
return 'xcb_popcount(' + _c_accessor_get_expr(expr.rhs, field_mapping) + ')'
|
||||
elif expr.op == 'enumref':
|
||||
if expr.op == '~': # SAFE
|
||||
return '(' + '~' + _c_accessor_get_expr(expr.rhs, field_mapping, checked=checked) + ')'
|
||||
elif expr.op == 'popcount': # SAFE
|
||||
return 'xcb_popcount(' + _c_accessor_get_expr(expr.rhs, field_mapping, checked=checked) + ')'
|
||||
elif expr.op == 'enumref': # SAFE
|
||||
enum_name = expr.lenfield_type.name
|
||||
constant_name = expr.lenfield_name
|
||||
c_name = _n(enum_name + (constant_name,)).upper()
|
||||
|
@ -1697,7 +1764,7 @@ def _c_accessor_get_expr(expr, field_mapping):
|
|||
field = expr.lenfield
|
||||
list_name = field_mapping[field.c_field_name][0]
|
||||
c_length_func = "%s(%s)" % (field.c_length_name, list_name)
|
||||
c_length_func = _c_accessor_get_expr(field.type.expr, field_mapping)
|
||||
c_length_func = _c_accessor_get_expr(field.type.expr, field_mapping, checked=checked)
|
||||
# create explicit code for computing the sum.
|
||||
# This works for all C-types which can be added to int64_t with +=
|
||||
_c_pre.start()
|
||||
|
@ -1742,11 +1809,16 @@ def _c_accessor_get_expr(expr, field_mapping):
|
|||
# cause pre-code of the subexpression be added right here
|
||||
_c_pre.end()
|
||||
# compute the subexpression
|
||||
rhs_expr_str = _c_accessor_get_expr(expr.rhs, scoped_field_mapping)
|
||||
rhs_expr_str = _c_accessor_get_expr(expr.rhs, scoped_field_mapping, checked=checked)
|
||||
# resume with our code
|
||||
_c_pre.start()
|
||||
# output the summation expression
|
||||
_c_pre.code("%s += %s;", sumvar, rhs_expr_str)
|
||||
if checked:
|
||||
_c_pre.code("if (__builtin_add_overflow(%s, %s, &%s))", sumvar, rhs_expr_str, sumvar)
|
||||
_c_pre.code(" /* SUMOF */")
|
||||
_c_pre.code(" return -1;")
|
||||
else:
|
||||
_c_pre.code("%s += %s;", sumvar, rhs_expr_str)
|
||||
|
||||
_c_pre.code("%s++;", listvar)
|
||||
_c_pre.pop_indent()
|
||||
|
@ -1757,9 +1829,39 @@ def _c_accessor_get_expr(expr, field_mapping):
|
|||
elif expr.op == 'listelement-ref':
|
||||
return '(*xcb_listelement)'
|
||||
elif expr.op != None and expr.op != 'calculate_len':
|
||||
return ('(' + _c_accessor_get_expr(expr.lhs, field_mapping) +
|
||||
' ' + expr.op + ' ' +
|
||||
_c_accessor_get_expr(expr.rhs, field_mapping) + ')')
|
||||
_c_pre.start()
|
||||
sumvar = _c_pre.get_tempvarname()
|
||||
match expr.op:
|
||||
case '+':
|
||||
op = 'add'
|
||||
case '-':
|
||||
op = 'sub'
|
||||
case '*':
|
||||
op = 'mul'
|
||||
case '/':
|
||||
_c_pre.tempvar("int64_t %s;", sumvar)
|
||||
_c_pre.code("%s = (%s);",
|
||||
sumvar,
|
||||
_c_accessor_get_expr(expr.rhs, field_mapping, checked=checked))
|
||||
_c_pre.code("if (%s < 1)", sumvar)
|
||||
_c_pre.code(" return -1;" if checked else " abort();")
|
||||
_c_pre.end()
|
||||
return ('((' + _c_accessor_get_expr(expr.lhs, field_mapping, checked=checked) + ') / ' + sumvar + ')')
|
||||
case '&':
|
||||
_c_pre.end()
|
||||
return ('(' + _c_accessor_get_expr(expr.lhs, field_mapping, checked=checked) +
|
||||
' & ' + _c_accessor_get_expr(expr.rhs, field_mapping, checked=checked) + ')')
|
||||
case _:
|
||||
assert False
|
||||
_c_pre.tempvar("int64_t %s; /* sum */", sumvar)
|
||||
_c_pre.code("if (__builtin_%s_overflow((%s), (%s), &%s))",
|
||||
op,
|
||||
_c_accessor_get_expr(expr.lhs, field_mapping, checked=checked),
|
||||
_c_accessor_get_expr(expr.rhs, field_mapping, checked=checked),
|
||||
sumvar)
|
||||
_c_pre.code(" return -1;" if checked else " abort();")
|
||||
_c_pre.end()
|
||||
return ('(' + sumvar + ')')
|
||||
elif expr.bitfield:
|
||||
return 'xcb_popcount(' + lenexp + ')'
|
||||
else:
|
||||
|
@ -1971,7 +2073,7 @@ def _c_accessors_list(self, field):
|
|||
(field.c_field_name)
|
||||
);
|
||||
else:
|
||||
return _c_accessor_get_expr(field.type.expr, fields)
|
||||
return _c_accessor_get_expr(field.type.expr, fields, checked=False)
|
||||
|
||||
_c(' return %s;', get_length())
|
||||
_c('}')
|
||||
|
@ -2152,7 +2254,7 @@ def _c_complex(self, force_packed = False):
|
|||
if b.type.has_name:
|
||||
_h(' } %s;', b.c_field_name)
|
||||
|
||||
_h('} %s%s;', 'XCB_PACKED ' if force_packed else '', self.c_type)
|
||||
_h('} %sXCB_MAY_ALIAS %s;', 'XCB_PACKED ' if force_packed else '', self.c_type)
|
||||
|
||||
def c_struct(self, name):
|
||||
'''
|
||||
|
@ -2367,7 +2469,7 @@ def _c_request_helper(self, name, void, regular, aux=False, reply_fds=False):
|
|||
_c(' void *xcb_aux%d = 0;' % (idx))
|
||||
if list_with_var_size_elems:
|
||||
_c(' unsigned int xcb_tmp_len;')
|
||||
_c(' char *xcb_tmp;')
|
||||
_c(' const char *xcb_tmp;')
|
||||
|
||||
num_fds_fixed = 0
|
||||
num_fds_expr = []
|
||||
|
@ -2376,7 +2478,7 @@ def _c_request_helper(self, name, void, regular, aux=False, reply_fds=False):
|
|||
if not field.type.is_list:
|
||||
num_fds_fixed += 1
|
||||
else:
|
||||
num_fds_expr.append(_c_accessor_get_expr(field.type.expr, None))
|
||||
num_fds_expr.append(_c_accessor_get_expr(field.type.expr, None, checked=True))
|
||||
|
||||
if list_with_var_size_elems or len(num_fds_expr) > 0:
|
||||
_c(' unsigned int i;')
|
||||
|
@ -2396,7 +2498,7 @@ def _c_request_helper(self, name, void, regular, aux=False, reply_fds=False):
|
|||
for field in wire_fields:
|
||||
if field.type.fixed_size():
|
||||
if field.type.is_expr:
|
||||
_c(' xcb_out.%s = %s;', field.c_field_name, _c_accessor_get_expr(field.type.expr, None))
|
||||
_c(' xcb_out.%s = %s;', field.c_field_name, _c_accessor_get_expr(field.type.expr, None, checked=True))
|
||||
elif field.type.is_pad:
|
||||
if field.type.nmemb == 1:
|
||||
_c(' xcb_out.%s = 0;', field.c_field_name)
|
||||
|
@ -2437,12 +2539,12 @@ def _c_request_helper(self, name, void, regular, aux=False, reply_fds=False):
|
|||
if field.type.expr.op == 'calculate_len':
|
||||
lenfield = field.type.expr.lenfield_name
|
||||
else:
|
||||
lenfield = _c_accessor_get_expr(field.type.expr, None)
|
||||
lenfield = _c_accessor_get_expr(field.type.expr, None, checked=False)
|
||||
|
||||
_c(' xcb_parts[%d].iov_len = %s * sizeof(%s);', count, lenfield,
|
||||
field.type.member.c_wiretype)
|
||||
else:
|
||||
list_length = _c_accessor_get_expr(field.type.expr, None)
|
||||
list_length = _c_accessor_get_expr(field.type.expr, None, checked=False)
|
||||
length = ''
|
||||
|
||||
_c(" xcb_parts[%d].iov_len = 0;" % count)
|
||||
|
@ -2500,7 +2602,7 @@ def _c_request_helper(self, name, void, regular, aux=False, reply_fds=False):
|
|||
if not field.type.is_list:
|
||||
_c(' fds[fd_index++] = %s;', field.c_field_name)
|
||||
else:
|
||||
_c(' for (i = 0; i < %s; i++)', _c_accessor_get_expr(field.type.expr, None))
|
||||
_c(' for (i = 0; i < %s; i++)', _c_accessor_get_expr(field.type.expr, None, checked=False))
|
||||
_c(' fds[fd_index++] = %s[i];', field.c_field_name)
|
||||
|
||||
if not num_fds:
|
||||
|
@ -2566,34 +2668,39 @@ def _c_reply(self, name):
|
|||
_h('%sxcb_generic_error_t%s **e);', spacing3, spacing2)
|
||||
_c('%sxcb_generic_error_t%s **e)', spacing3, spacing2)
|
||||
_c('{')
|
||||
|
||||
if len(unserialize_fields)>0:
|
||||
# certain variable size fields need to be unserialized explicitly
|
||||
_c(' %s *reply = (%s *) xcb_wait_for_reply(c, cookie.sequence, e);',
|
||||
if not self.reply.fixed_size:
|
||||
_c(' %s *reply = (%s *) xcb_wait_for_reply_safe(c, cookie.sequence, e, sizeof (*reply));',
|
||||
self.c_reply_type, self.c_reply_type)
|
||||
_c(' int i;')
|
||||
for field in unserialize_fields:
|
||||
if field.type.is_list:
|
||||
_c(' %s %s_iter = %s(reply);', field.c_iterator_type, field.c_field_name, field.c_iterator_name)
|
||||
_c(' int %s_len = %s(reply);', field.c_field_name, field.c_length_name)
|
||||
_c(' %s *%s_data;', field.c_field_type, field.c_field_name)
|
||||
else:
|
||||
raise Exception('not implemented: call _unserialize() in reply for non-list type %s', field.c_field_type)
|
||||
# call _unserialize(), using the reply as source and target buffer
|
||||
_c(' /* special cases: transform parts of the reply to match XCB data structures */')
|
||||
for field in unserialize_fields:
|
||||
if field.type.is_list:
|
||||
_c(' for(i=0; i<%s_len; i++) {', field.c_field_name)
|
||||
_c(' %s_data = %s_iter.data;', field.c_field_name, field.c_field_name)
|
||||
_c(' %s((const void *)%s_data, &%s_data);', field.type.c_unserialize_name,
|
||||
field.c_field_name, field.c_field_name)
|
||||
_c(' %s(&%s_iter);', field.type.c_next_name, field.c_field_name)
|
||||
_c(' }')
|
||||
# return the transformed reply
|
||||
_c(' if (!reply)')
|
||||
_c(' return NULL;')
|
||||
_c(' if (%s_checked(reply) < 0) {', self.c_sizeof_name)
|
||||
_c(' free(reply);')
|
||||
_c(' return NULL;')
|
||||
_c(' }')
|
||||
if len(unserialize_fields)>0:
|
||||
# certain variable size fields need to be unserialized explicitly
|
||||
_c(' int i;')
|
||||
for field in unserialize_fields:
|
||||
if field.type.is_list:
|
||||
_c(' %s %s_iter = %s(reply);', field.c_iterator_type, field.c_field_name, field.c_iterator_name)
|
||||
_c(' int %s_len = %s(reply);', field.c_field_name, field.c_length_name)
|
||||
_c(' %s *%s_data;', field.c_field_type, field.c_field_name)
|
||||
else:
|
||||
raise Exception('not implemented: call _unserialize() in reply for non-list type %s', field.c_field_type)
|
||||
# call _unserialize(), using the reply as source and target buffer
|
||||
_c(' /* special cases: transform parts of the reply to match XCB data structures */')
|
||||
for field in unserialize_fields:
|
||||
if field.type.is_list:
|
||||
_c(' for(i=0; i<%s_len; i++) {', field.c_field_name)
|
||||
_c(' %s_data = %s_iter.data;', field.c_field_name, field.c_field_name)
|
||||
_c(' %s((const void *)%s_data, &%s_data);', field.type.c_unserialize_name,
|
||||
field.c_field_name, field.c_field_name)
|
||||
_c(' %s(&%s_iter);', field.type.c_next_name, field.c_field_name)
|
||||
_c(' }')
|
||||
# return the transformed reply
|
||||
_c(' return reply;')
|
||||
|
||||
else:
|
||||
_c(' return (%s *) xcb_wait_for_reply(c, cookie.sequence, e);', self.c_reply_type)
|
||||
_c(' return (%s *) xcb_wait_for_reply_safe(c, cookie.sequence, e, sizeof (%s));', self.c_reply_type, self.c_reply_type)
|
||||
|
||||
_c('}')
|
||||
|
||||
|
@ -2649,7 +2756,7 @@ def _c_cookie(self, name):
|
|||
_h(' **/')
|
||||
_h('typedef struct %s {', self.c_cookie_type)
|
||||
_h(' unsigned int sequence;')
|
||||
_h('} %s;', self.c_cookie_type)
|
||||
_h('} XCB_MAY_ALIAS %s;', self.c_cookie_type)
|
||||
|
||||
def _man_request(self, name, void, aux):
|
||||
param_fields = [f for f in self.fields if f.visible]
|
||||
|
@ -2705,7 +2812,7 @@ def _man_request(self, name, void, aux):
|
|||
f.write('.SS Reply datastructure\n')
|
||||
f.write('.nf\n')
|
||||
f.write('.sp\n')
|
||||
f.write('typedef %s %s {\n' % (self.reply.c_container, self.reply.c_type))
|
||||
f.write('typedef XCB_MAY_ALIAS %s %s {\n' % (self.reply.c_container, self.reply.c_type))
|
||||
struct_fields = []
|
||||
maxtypelen = 0
|
||||
|
||||
|
@ -3282,7 +3389,7 @@ def c_event(self, name):
|
|||
else:
|
||||
# Typedef
|
||||
_h('')
|
||||
_h('typedef %s %s;', _t(self.name + ('event',)), _t(name + ('event',)))
|
||||
_h('typedef XCB_MAY_ALIAS %s %s;', _t(self.name + ('event',)), _t(name + ('event',)))
|
||||
|
||||
# Create sizeof-function for eventcopies for compatibility reasons
|
||||
if self.c_need_sizeof:
|
||||
|
@ -3317,7 +3424,7 @@ def c_error(self, name):
|
|||
else:
|
||||
# Typedef
|
||||
_h('')
|
||||
_h('typedef %s %s;', _t(self.name + ('error',)), _t(name + ('error',)))
|
||||
_h('typedef XCB_MAY_ALIAS %s %s;', _t(self.name + ('error',)), _t(name + ('error',)))
|
||||
|
||||
|
||||
# Main routine starts here
|
||||
|
|
|
@ -66,8 +66,14 @@ extern "C" {
|
|||
/* Supported in gcc 2.7 and later */
|
||||
#if defined(__GNUC__) || __has_attribute(__packed__)
|
||||
#define XCB_PACKED __attribute__((__packed__))
|
||||
#ifndef XCB_MAY_ALIAS
|
||||
#define XCB_MAY_ALIAS __attribute__((may_alias))
|
||||
#endif
|
||||
#else
|
||||
#define XCB_PACKED
|
||||
#ifndef XCB_MAY_ALIAS
|
||||
#define XCB_MAY_ALIAS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Supported in gcc 2.96 and later */
|
||||
|
|
174
src/xcb_conn.c
174
src/xcb_conn.c
|
@ -30,12 +30,14 @@
|
|||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdalign.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "xcb.h"
|
||||
#include "xcbint.h"
|
||||
|
@ -168,8 +170,23 @@ static int write_setup(xcb_connection_t *c, xcb_auth_info_t *auth_info)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* A bunch of static assertions */
|
||||
#define ASSERT_SIZE_ALIGN(t, size, align) \
|
||||
static_assert(sizeof(t) == size, "unexpected size of" #t); \
|
||||
static_assert(alignof(t) == align, "unexpected alignment of" #t);
|
||||
ASSERT_SIZE_ALIGN(xcb_screen_t, 40, 4);
|
||||
ASSERT_SIZE_ALIGN(xcb_setup_t, 40, 4);
|
||||
ASSERT_SIZE_ALIGN(xcb_setup_generic_t, 8, 2);
|
||||
ASSERT_SIZE_ALIGN(xcb_setup_authenticate_t, 8, 2);
|
||||
ASSERT_SIZE_ALIGN(xcb_setup_failed_t, 8, 2);
|
||||
ASSERT_SIZE_ALIGN(xcb_visualtype_t, 24, 4);
|
||||
ASSERT_SIZE_ALIGN(xcb_depth_t, 8, 2);
|
||||
ASSERT_SIZE_ALIGN(xcb_format_t, 8, 1);
|
||||
#undef ASSERT_SIZE_ALIGN
|
||||
|
||||
static int read_setup(xcb_connection_t *c)
|
||||
{
|
||||
uint32_t extra_bytes, total_bytes;
|
||||
const char newline = '\n';
|
||||
|
||||
/* Read the server response */
|
||||
|
@ -180,14 +197,16 @@ static int read_setup(xcb_connection_t *c)
|
|||
if(_xcb_in_read_block(c, c->setup, sizeof(xcb_setup_generic_t)) != sizeof(xcb_setup_generic_t))
|
||||
return 0;
|
||||
|
||||
extra_bytes = c->setup->length * UINT32_C(4);
|
||||
total_bytes = extra_bytes + sizeof(xcb_setup_generic_t);
|
||||
{
|
||||
void *tmp = realloc(c->setup, c->setup->length * 4 + sizeof(xcb_setup_generic_t));
|
||||
void *tmp = realloc(c->setup, total_bytes);
|
||||
if(!tmp)
|
||||
return 0;
|
||||
c->setup = tmp;
|
||||
}
|
||||
|
||||
if(_xcb_in_read_block(c, (char *) c->setup + sizeof(xcb_setup_generic_t), c->setup->length * 4) <= 0)
|
||||
if(_xcb_in_read_block(c, (char *) c->setup + sizeof(xcb_setup_generic_t), extra_bytes) <= 0)
|
||||
return 0;
|
||||
|
||||
/* 0 = failed, 2 = authenticate, 1 = success */
|
||||
|
@ -196,21 +215,162 @@ static int read_setup(xcb_connection_t *c)
|
|||
case 0: /* failed */
|
||||
{
|
||||
xcb_setup_failed_t *setup = (xcb_setup_failed_t *) c->setup;
|
||||
write(STDERR_FILENO, xcb_setup_failed_reason(setup), xcb_setup_failed_reason_length(setup));
|
||||
char *msg = (char *)c->setup + sizeof(*setup);
|
||||
if (setup->reason_len > extra_bytes)
|
||||
setup->reason_len = (uint8_t)extra_bytes;
|
||||
for (size_t i = 0; i < setup->reason_len; ++i) {
|
||||
if (msg[i] < 0x20 || msg[i] > 0x7E)
|
||||
msg[i] = 0x20;
|
||||
}
|
||||
write(STDERR_FILENO, msg, setup->reason_len);
|
||||
write(STDERR_FILENO, &newline, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case 2: /* authenticate */
|
||||
{
|
||||
xcb_setup_authenticate_t *setup = (xcb_setup_authenticate_t *) c->setup;
|
||||
write(STDERR_FILENO, xcb_setup_authenticate_reason(setup), xcb_setup_authenticate_reason_length(setup));
|
||||
char *reason = (char *)c->setup + sizeof(xcb_setup_authenticate_t);
|
||||
for (size_t i = 0; i < extra_bytes; ++i) {
|
||||
if (reason[i] < 0x20 || reason[i] > 0x7E)
|
||||
reason[i] = 0x20;
|
||||
}
|
||||
write(STDERR_FILENO, reason, extra_bytes);
|
||||
write(STDERR_FILENO, &newline, 1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
case 1: /* success */
|
||||
{
|
||||
const uint8_t *cursor = (const uint8_t *)c->setup;
|
||||
const uint8_t *end = cursor + total_bytes;
|
||||
xcb_setup_t *const setup = (xcb_setup_t *)cursor;
|
||||
const xcb_format_t *formats;
|
||||
uint32_t pixmap_offset, pixmap_formats_len;
|
||||
size_t i, j;
|
||||
|
||||
return 1;
|
||||
if (total_bytes < sizeof(xcb_setup_t) + sizeof(xcb_screen_t) +
|
||||
sizeof(xcb_format_t))
|
||||
return 0; /* not enough bytes sent, no screens, or no formats */
|
||||
|
||||
if (setup->roots_len < 1 || setup->pixmap_formats_len < 1)
|
||||
return 0; /* no screens or no formats */
|
||||
|
||||
/* Offset of the pixmaps. Cannot exceed 65576. */
|
||||
static_assert(sizeof(setup->vendor_len) == 2,
|
||||
"wrong size of setup->vendor_len");
|
||||
pixmap_offset = (sizeof(*setup) + (uint32_t)setup->vendor_len + 3) & ~3;
|
||||
|
||||
/* Length of the pixmap formats. Cannot exceed 2040. */
|
||||
static_assert(sizeof(setup->pixmap_formats_len) == 1,
|
||||
"wrong size of setup->pixmap_formats_len");
|
||||
pixmap_formats_len = sizeof(xcb_format_t) * (uint32_t)setup->pixmap_formats_len;
|
||||
|
||||
/* Offset of the screens. Max RHS = 65576 + 2040 = 67616
|
||||
* so no risk of overflow */
|
||||
uint32_t const screen_offset = pixmap_offset + pixmap_formats_len;
|
||||
|
||||
/* 4 zero bytes, for memcmp() */
|
||||
uint32_t const zero = 0;
|
||||
|
||||
/* First screen must fit in the data sent. */
|
||||
if (total_bytes < screen_offset + sizeof(xcb_screen_t))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* xcb_setup_pixmap_formats() would be safe, but just using pointer
|
||||
* arithmetic is simpler.
|
||||
*/
|
||||
formats = (const xcb_format_t *)(cursor + pixmap_offset);
|
||||
|
||||
/* Validate the pixmap formats. Bounds have been checked already. */
|
||||
for (i = 0; i < setup->pixmap_formats_len; ++i) {
|
||||
/* Depth must not be zero. */
|
||||
if (formats[i].depth == 0)
|
||||
return 0;
|
||||
|
||||
/* Bits per pixel must not be zero and must be a multiple of 8. */
|
||||
if ((formats[i].bits_per_pixel < 8) ||
|
||||
(formats[i].bits_per_pixel % 8) != 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Alignment is guaranteed because screen_offset is a multiple of 4
|
||||
* and c->setup was allocated by malloc(). cursor is kept 4-byte
|
||||
* aligned at all times.
|
||||
*/
|
||||
cursor += screen_offset;
|
||||
|
||||
/* Validate each screen. */
|
||||
for (i = 0; i < setup->roots_len; ++i) {
|
||||
const struct xcb_screen_t *screen;
|
||||
const struct xcb_depth_t *depth;
|
||||
const struct xcb_visualtype_t *visuals;
|
||||
|
||||
/*
|
||||
* Screen must fit in the buffer with room for a depth and a
|
||||
* visual type.
|
||||
*/
|
||||
if ((end - cursor) <
|
||||
(ptrdiff_t)(sizeof(*screen) + sizeof(*depth) + sizeof(*visuals)))
|
||||
return 0;
|
||||
|
||||
screen = (const struct xcb_screen_t *)cursor;
|
||||
cursor += sizeof(*screen);
|
||||
|
||||
/* Screens must have at least one depth */
|
||||
if (screen->allowed_depths_len < 1)
|
||||
return 0;
|
||||
|
||||
for (j = 0; j < screen->allowed_depths_len; ++j) {
|
||||
/* Depth must fit in the buffer with room for a visual type. */
|
||||
if ((end - cursor) < (ptrdiff_t)(sizeof(*depth) + sizeof(*visuals)))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Alignment is guaranteed because xcb_screen_t has greater
|
||||
* alignment (4) than xcb_depth_t (2). sizeof(xcb_depth_t)
|
||||
* is a multiple of 4 so alignment will be maintained.
|
||||
*/
|
||||
depth = (const xcb_depth_t *)cursor;
|
||||
visuals = (const xcb_visualtype_t *)(depth + 1);
|
||||
|
||||
/* Padding must be zero */
|
||||
if ((depth->pad0 != 0) ||
|
||||
(memcmp(depth->pad1, &zero, sizeof(zero)) != 0))
|
||||
return 0;
|
||||
|
||||
/* Depths must have at least one visual type */
|
||||
if (depth->visuals_len < 1)
|
||||
return 0;
|
||||
|
||||
/* depth->visuals_len is uint16_t so overflow is impossible. */
|
||||
static_assert(sizeof(depth->visuals_len) == 2,
|
||||
"wrong size of setup->visuals_len");
|
||||
|
||||
/* Visuals must fit in the buffer. */
|
||||
if ((size_t)(end - (const uint8_t *)visuals) <
|
||||
(uint32_t)depth->visuals_len * sizeof(*visuals))
|
||||
return 0;
|
||||
cursor = (const uint8_t *)(visuals + depth->visuals_len);
|
||||
while ((const uint8_t *)visuals < cursor) {
|
||||
/* Padding must be zero. */
|
||||
if (memcmp(visuals->pad0, &zero, 4))
|
||||
return 0;
|
||||
|
||||
/* Bits per RGB value must not be zero. */
|
||||
if (visuals->bits_per_rgb_value < 1)
|
||||
return 0;
|
||||
visuals++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (end != cursor)
|
||||
return 0; /* trailing junk */
|
||||
}
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* precondition: there must be something for us to write. */
|
||||
|
|
178
src/xcb_in.c
178
src/xcb_in.c
|
@ -34,6 +34,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#if USE_POLL
|
||||
#include <poll.h>
|
||||
|
@ -251,7 +252,7 @@ static int read_packet(xcb_connection_t *c)
|
|||
|
||||
/* XXX a bit of a hack -- we "know" that all FD replys place
|
||||
* the number of fds in the pad0 byte */
|
||||
if (pend && pend->flags & XCB_REQUEST_REPLY_FDS)
|
||||
if (pend && (pend->flags & XCB_REQUEST_REPLY_FDS))
|
||||
nfd = genrep.pad0;
|
||||
}
|
||||
|
||||
|
@ -261,7 +262,7 @@ static int read_packet(xcb_connection_t *c)
|
|||
|
||||
bufsize = length + eventlength + nfd * sizeof(int) +
|
||||
(genrep.response_type == XCB_REPLY ? 0 : sizeof(uint32_t));
|
||||
if (bufsize < INT32_MAX)
|
||||
if (bufsize < INT32_MAX && length <= INT_MAX && eventlength <= INT_MAX)
|
||||
buf = malloc((size_t) bufsize);
|
||||
else
|
||||
buf = NULL;
|
||||
|
@ -415,6 +416,7 @@ static int poll_for_reply(xcb_connection_t *c, uint64_t request, void **reply, x
|
|||
{
|
||||
struct reply_list *head;
|
||||
|
||||
*reply = NULL;
|
||||
/* If an error occurred when issuing the request, fail immediately. */
|
||||
if(!request)
|
||||
head = 0;
|
||||
|
@ -446,7 +448,6 @@ static int poll_for_reply(xcb_connection_t *c, uint64_t request, void **reply, x
|
|||
|
||||
if(error)
|
||||
*error = 0;
|
||||
*reply = 0;
|
||||
|
||||
if(head)
|
||||
{
|
||||
|
@ -510,6 +511,12 @@ static void *wait_for_reply(xcb_connection_t *c, uint64_t request, xcb_generic_e
|
|||
{
|
||||
void *ret = 0;
|
||||
|
||||
if (e)
|
||||
*e = NULL;
|
||||
|
||||
if (c->has_error)
|
||||
return NULL;
|
||||
|
||||
/* If this request has not been written yet, write it. */
|
||||
if(c->out.return_socket || _xcb_out_flush_to(c, request))
|
||||
{
|
||||
|
@ -538,16 +545,24 @@ static uint64_t widen(xcb_connection_t *c, unsigned int request)
|
|||
return widened_request;
|
||||
}
|
||||
|
||||
static void *wait_for_reply_safe(xcb_connection_t *c, uint64_t request, xcb_generic_error_t **e, size_t size)
|
||||
{
|
||||
xcb_generic_reply_t *ret = NULL;
|
||||
ret = wait_for_reply(c, request, e);
|
||||
if (ret && size > 32 && ret->length < ((size - 29) >> 2))
|
||||
{
|
||||
_xcb_conn_shutdown(c, XCB_CONN_CLOSED_PARSE_ERR);
|
||||
free(ret);
|
||||
ret = NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Public interface */
|
||||
|
||||
void *xcb_wait_for_reply(xcb_connection_t *c, unsigned int request, xcb_generic_error_t **e)
|
||||
{
|
||||
void *ret;
|
||||
if(e)
|
||||
*e = 0;
|
||||
if(c->has_error)
|
||||
return 0;
|
||||
|
||||
pthread_mutex_lock(&c->iolock);
|
||||
ret = wait_for_reply(c, widen(c, request), e);
|
||||
pthread_mutex_unlock(&c->iolock);
|
||||
|
@ -557,17 +572,30 @@ void *xcb_wait_for_reply(xcb_connection_t *c, unsigned int request, xcb_generic_
|
|||
void *xcb_wait_for_reply64(xcb_connection_t *c, uint64_t request, xcb_generic_error_t **e)
|
||||
{
|
||||
void *ret;
|
||||
if(e)
|
||||
*e = 0;
|
||||
if(c->has_error)
|
||||
return 0;
|
||||
|
||||
pthread_mutex_lock(&c->iolock);
|
||||
ret = wait_for_reply(c, request, e);
|
||||
pthread_mutex_unlock(&c->iolock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *xcb_wait_for_reply_safe(xcb_connection_t *c, unsigned int request, xcb_generic_error_t **e, size_t size)
|
||||
{
|
||||
xcb_generic_reply_t *ret = NULL;
|
||||
pthread_mutex_lock(&c->iolock);
|
||||
ret = wait_for_reply_safe(c, widen(c, request), e, size);
|
||||
pthread_mutex_unlock(&c->iolock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *xcb_wait_for_reply64_safe(xcb_connection_t *c, uint64_t request, xcb_generic_error_t **e, size_t size)
|
||||
{
|
||||
xcb_generic_reply_t *ret = NULL;
|
||||
pthread_mutex_lock(&c->iolock);
|
||||
ret = wait_for_reply_safe(c, request, e, size);
|
||||
pthread_mutex_unlock(&c->iolock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int *xcb_get_reply_fds(xcb_connection_t *c, void *reply, size_t reply_size)
|
||||
{
|
||||
return (int *) (&((char *) reply)[reply_size]);
|
||||
|
@ -599,6 +627,9 @@ static void discard_reply(xcb_connection_t *c, uint64_t request)
|
|||
void *reply;
|
||||
pending_reply **prev_pend;
|
||||
|
||||
if (c->has_error)
|
||||
return;
|
||||
|
||||
/* Free any replies or errors that we've already read. Stop if
|
||||
* xcb_wait_for_reply would block or we've run out of replies. */
|
||||
while(poll_for_reply(c, request, &reply, 0) && reply)
|
||||
|
@ -628,9 +659,6 @@ static void discard_reply(xcb_connection_t *c, uint64_t request)
|
|||
|
||||
void xcb_discard_reply(xcb_connection_t *c, unsigned int sequence)
|
||||
{
|
||||
if(c->has_error)
|
||||
return;
|
||||
|
||||
/* If an error occurred when issuing the request, fail immediately. */
|
||||
if(!sequence)
|
||||
return;
|
||||
|
@ -642,9 +670,6 @@ void xcb_discard_reply(xcb_connection_t *c, unsigned int sequence)
|
|||
|
||||
void xcb_discard_reply64(xcb_connection_t *c, uint64_t sequence)
|
||||
{
|
||||
if(c->has_error)
|
||||
return;
|
||||
|
||||
/* If an error occurred when issuing the request, fail immediately. */
|
||||
if(!sequence)
|
||||
return;
|
||||
|
@ -654,9 +679,10 @@ void xcb_discard_reply64(xcb_connection_t *c, uint64_t sequence)
|
|||
pthread_mutex_unlock(&c->iolock);
|
||||
}
|
||||
|
||||
int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply, xcb_generic_error_t **error)
|
||||
static int poll_for_reply_inner(xcb_connection_t *c, uint64_t request, void **reply, xcb_generic_error_t **error)
|
||||
{
|
||||
int ret;
|
||||
assert(reply != 0);
|
||||
if(c->has_error)
|
||||
{
|
||||
*reply = 0;
|
||||
|
@ -664,11 +690,33 @@ int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply,
|
|||
*error = 0;
|
||||
return 1; /* would not block */
|
||||
}
|
||||
assert(reply != 0);
|
||||
pthread_mutex_lock(&c->iolock);
|
||||
ret = poll_for_reply(c, widen(c, request), reply, error);
|
||||
ret = poll_for_reply(c, request, reply, error);
|
||||
if(!ret && c->in.reading == 0 && _xcb_in_read(c)) /* _xcb_in_read shuts down the connection on error */
|
||||
ret = poll_for_reply(c, widen(c, request), reply, error);
|
||||
ret = poll_for_reply(c, request, reply, error);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int poll_for_reply_inner_safe(xcb_connection_t *c, uint64_t request, void **reply, xcb_generic_error_t **error, size_t size)
|
||||
{
|
||||
int ret = poll_for_reply_inner(c, request, reply, error);
|
||||
if (ret && *reply)
|
||||
{
|
||||
xcb_generic_reply_t *genrep = *reply;
|
||||
if (size > 32 && genrep->length < ((size - 29) >> 2))
|
||||
{
|
||||
_xcb_conn_shutdown(c, XCB_CONN_CLOSED_PARSE_ERR);
|
||||
free(genrep);
|
||||
*reply = NULL;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply, xcb_generic_error_t **error)
|
||||
{
|
||||
int ret;
|
||||
pthread_mutex_lock(&c->iolock);
|
||||
ret = poll_for_reply_inner(c, widen(c, request), reply, error);
|
||||
pthread_mutex_unlock(&c->iolock);
|
||||
return ret;
|
||||
}
|
||||
|
@ -676,34 +724,43 @@ int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply,
|
|||
int xcb_poll_for_reply64(xcb_connection_t *c, uint64_t request, void **reply, xcb_generic_error_t **error)
|
||||
{
|
||||
int ret;
|
||||
if(c->has_error)
|
||||
{
|
||||
*reply = 0;
|
||||
if(error)
|
||||
*error = 0;
|
||||
return 1; /* would not block */
|
||||
}
|
||||
assert(reply != 0);
|
||||
pthread_mutex_lock(&c->iolock);
|
||||
ret = poll_for_reply(c, request, reply, error);
|
||||
if(!ret && c->in.reading == 0 && _xcb_in_read(c)) /* _xcb_in_read shuts down the connection on error */
|
||||
ret = poll_for_reply(c, request, reply, error);
|
||||
ret = poll_for_reply_inner(c, request, reply, error);
|
||||
pthread_mutex_unlock(&c->iolock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xcb_poll_for_reply_safe(xcb_connection_t *c, unsigned int request, void **reply, xcb_generic_error_t **error, size_t size)
|
||||
{
|
||||
int ret;
|
||||
pthread_mutex_lock(&c->iolock);
|
||||
ret = poll_for_reply_inner_safe(c, widen(c, request), reply, error, size);
|
||||
pthread_mutex_unlock(&c->iolock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xcb_poll_for_reply64_safe(xcb_connection_t *c, uint64_t request, void **reply, xcb_generic_error_t **error, size_t size)
|
||||
{
|
||||
int ret;
|
||||
pthread_mutex_lock(&c->iolock);
|
||||
ret = poll_for_reply_inner_safe(c, request, reply, error, size);
|
||||
pthread_mutex_unlock(&c->iolock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
xcb_generic_event_t *xcb_wait_for_event(xcb_connection_t *c)
|
||||
{
|
||||
xcb_generic_event_t *ret;
|
||||
if(c->has_error)
|
||||
return 0;
|
||||
xcb_generic_event_t *ret = NULL;
|
||||
pthread_mutex_lock(&c->iolock);
|
||||
/* get_event returns 0 on empty list. */
|
||||
while(!(ret = get_event(c)))
|
||||
if(!_xcb_conn_wait(c, &c->in.event_cond, 0, 0))
|
||||
break;
|
||||
if(!c->has_error)
|
||||
{
|
||||
/* get_event returns 0 on empty list. */
|
||||
while(!(ret = get_event(c)))
|
||||
if(!_xcb_conn_wait(c, &c->in.event_cond, 0, 0))
|
||||
break;
|
||||
|
||||
_xcb_in_wake_up_next_reader(c);
|
||||
_xcb_in_wake_up_next_reader(c);
|
||||
}
|
||||
pthread_mutex_unlock(&c->iolock);
|
||||
return ret;
|
||||
}
|
||||
|
@ -711,15 +768,15 @@ xcb_generic_event_t *xcb_wait_for_event(xcb_connection_t *c)
|
|||
static xcb_generic_event_t *poll_for_next_event(xcb_connection_t *c, int queued)
|
||||
{
|
||||
xcb_generic_event_t *ret = 0;
|
||||
pthread_mutex_lock(&c->iolock);
|
||||
if(!c->has_error)
|
||||
{
|
||||
pthread_mutex_lock(&c->iolock);
|
||||
/* FIXME: follow X meets Z architecture changes. */
|
||||
ret = get_event(c);
|
||||
if(!ret && !queued && c->in.reading == 0 && _xcb_in_read(c)) /* _xcb_in_read shuts down the connection on error */
|
||||
ret = get_event(c);
|
||||
pthread_mutex_unlock(&c->iolock);
|
||||
}
|
||||
pthread_mutex_unlock(&c->iolock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -738,9 +795,11 @@ xcb_generic_error_t *xcb_request_check(xcb_connection_t *c, xcb_void_cookie_t co
|
|||
uint64_t request;
|
||||
xcb_generic_error_t *ret = 0;
|
||||
void *reply;
|
||||
if(c->has_error)
|
||||
return 0;
|
||||
pthread_mutex_lock(&c->iolock);
|
||||
if(c->has_error) {
|
||||
pthread_mutex_unlock(&c->iolock);
|
||||
return 0;
|
||||
}
|
||||
request = widen(c, cookie.sequence);
|
||||
if (XCB_SEQUENCE_COMPARE(request, >, c->in.request_completed))
|
||||
{
|
||||
|
@ -779,9 +838,11 @@ xcb_generic_event_t *xcb_poll_for_special_event(xcb_connection_t *c,
|
|||
{
|
||||
xcb_generic_event_t *event;
|
||||
|
||||
if(c->has_error)
|
||||
return 0;
|
||||
pthread_mutex_lock(&c->iolock);
|
||||
if(c->has_error) {
|
||||
pthread_mutex_unlock(&c->iolock);
|
||||
return 0;
|
||||
}
|
||||
event = get_special_event(c, se);
|
||||
if(!event && c->in.reading == 0 && _xcb_in_read(c)) /* _xcb_in_read shuts down the connection on error */
|
||||
event = get_special_event(c, se);
|
||||
|
@ -795,9 +856,11 @@ xcb_generic_event_t *xcb_wait_for_special_event(xcb_connection_t *c,
|
|||
special_list special;
|
||||
xcb_generic_event_t *event;
|
||||
|
||||
if(c->has_error)
|
||||
return 0;
|
||||
pthread_mutex_lock(&c->iolock);
|
||||
if(c->has_error) {
|
||||
pthread_mutex_unlock(&c->iolock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
insert_special(&c->in.special_waiters, &special, se);
|
||||
|
||||
|
@ -822,12 +885,14 @@ xcb_register_for_special_xge(xcb_connection_t *c,
|
|||
xcb_special_event_t *se;
|
||||
const xcb_query_extension_reply_t *ext_reply;
|
||||
|
||||
if(c->has_error)
|
||||
return NULL;
|
||||
ext_reply = xcb_get_extension_data(c, ext);
|
||||
if (!ext_reply)
|
||||
return NULL;
|
||||
pthread_mutex_lock(&c->iolock);
|
||||
if (c->has_error) {
|
||||
pthread_mutex_unlock(&c->iolock);
|
||||
return NULL;
|
||||
}
|
||||
for (se = c->in.special_events; se; se = se->next) {
|
||||
if (se->extension == ext_reply->major_opcode &&
|
||||
se->eid == eid) {
|
||||
|
@ -866,10 +931,11 @@ xcb_unregister_for_special_event(xcb_connection_t *c,
|
|||
if (!se)
|
||||
return;
|
||||
|
||||
if (c->has_error)
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&c->iolock);
|
||||
if (c->has_error) {
|
||||
pthread_mutex_unlock(&c->iolock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (prev = &c->in.special_events; (s = *prev) != NULL; prev = &(s->next)) {
|
||||
if (s == se) {
|
||||
|
|
79
src/xcbext.h
79
src/xcbext.h
|
@ -207,7 +207,7 @@ void xcb_send_fd(xcb_connection_t *c, int fd);
|
|||
* All replies that are generated while the socket is owned externally have
|
||||
* @p flags applied to them. For example, use XCB_REQUEST_CHECK if you don't
|
||||
* want errors to go to xcb's normal error handling, but instead having to be
|
||||
* picked up via xcb_wait_for_reply(), xcb_poll_for_reply() or
|
||||
* picked up via xcb_wait_for_reply_safe(), xcb_poll_for_reply_safe() or
|
||||
* xcb_request_check().
|
||||
*/
|
||||
int xcb_take_socket(xcb_connection_t *c, void (*return_socket)(void *closure), void *closure, int flags, uint64_t *sent);
|
||||
|
@ -236,11 +236,27 @@ int xcb_writev(xcb_connection_t *c, struct iovec *vector, int count, uint64_t re
|
|||
|
||||
/* xcb_in.c */
|
||||
|
||||
/**
|
||||
* @brief Wait for the reply of a given request, with bounds checking.
|
||||
* @param c The connection to the X server.
|
||||
* @param request Sequence number of the request as returned by xcb_send_request().
|
||||
* @param e Location to store errors in, or NULL. Ignored for unchecked requests.
|
||||
* @param min_size The minimum size of the reply, in bytes.
|
||||
*
|
||||
* Returns the reply to the given request or returns null in the event of
|
||||
* errors. Blocks until the reply or error for the request arrives, or an I/O
|
||||
* error occurs. The returned pointer is guaranteed to point to at least min_size
|
||||
* bytes of data.
|
||||
*/
|
||||
void *xcb_wait_for_reply_safe(xcb_connection_t *c, unsigned int request,
|
||||
xcb_generic_error_t **e, size_t min_size);
|
||||
|
||||
/**
|
||||
* @brief Wait for the reply of a given request.
|
||||
* @param c The connection to the X server.
|
||||
* @param request Sequence number of the request as returned by xcb_send_request().
|
||||
* @param e Location to store errors in, or NULL. Ignored for unchecked requests.
|
||||
* @deprecated Does not perform bounds checks; use xcb_wait_for_reply_safe() instead.
|
||||
*
|
||||
* Returns the reply to the given request or returns null in the event of
|
||||
* errors. Blocks until the reply or error for the request arrives, or an I/O
|
||||
|
@ -253,6 +269,28 @@ void *xcb_wait_for_reply(xcb_connection_t *c, unsigned int request, xcb_generic_
|
|||
* @param c The connection to the X server.
|
||||
* @param request 64-bit sequence number of the request as returned by xcb_send_request64().
|
||||
* @param e Location to store errors in, or NULL. Ignored for unchecked requests.
|
||||
* @param size The number of bytes expected in the reply. If the reply is too short,
|
||||
* the connection will be shut down. If the return value is not null, it is guaranteed to
|
||||
* point to size bytes of memory.
|
||||
*
|
||||
* Returns the reply to the given request or returns null in the event of
|
||||
* errors. Blocks until the reply or error for the request arrives, or an I/O
|
||||
* error occurs.
|
||||
*
|
||||
* Unlike its xcb_wait_for_reply_safe() counterpart, the given sequence number is not
|
||||
* automatically "widened" to 64-bit.
|
||||
*/
|
||||
void *xcb_wait_for_reply64_safe(xcb_connection_t *c, uint64_t request,
|
||||
xcb_generic_error_t **e, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Wait for the reply of a given request, with 64-bit sequence number
|
||||
* @param c The connection to the X server.
|
||||
* @param request 64-bit sequence number of the request as returned by xcb_send_request64().
|
||||
* @param e Location to store errors in, or NULL. Ignored for unchecked requests.
|
||||
* @deprecated This function does not perform bounds checks. If reply is not NULL on
|
||||
* return, it is only guaranteed to point to 32 bytes of memory. Use
|
||||
* xcb_wait_for_reply64_safe() instead.
|
||||
*
|
||||
* Returns the reply to the given request or returns null in the event of
|
||||
* errors. Blocks until the reply or error for the request arrives, or an I/O
|
||||
|
@ -269,10 +307,47 @@ void *xcb_wait_for_reply64(xcb_connection_t *c, uint64_t request, xcb_generic_er
|
|||
* @param request Sequence number of the request as returned by xcb_send_request().
|
||||
* @param reply Location to store the reply in, must not be NULL.
|
||||
* @param error Location to store errors in, or NULL. Ignored for unchecked requests.
|
||||
* @param size The number of bytes expected in the reply. If the reply is too short,
|
||||
* the connection will be shut down. If reply points to a non-NULL pointer on return,
|
||||
* that pointer is guaranteed to point to size bytes of memory, or 32 bytes if size is
|
||||
* less than 32.
|
||||
* @return 1 when the reply to the request was returned, else 0.
|
||||
*
|
||||
* Checks if the reply to the given request already received. Does not block.
|
||||
*/
|
||||
int xcb_poll_for_reply_safe(xcb_connection_t *c, unsigned int request, void **reply, xcb_generic_error_t **error, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Poll for the reply of a given request, with 64-bit sequence number.
|
||||
* @param c The connection to the X server.
|
||||
* @param request 64-bit sequence number of the request as returned by xcb_send_request().
|
||||
* @param reply Location to store the reply in, must not be NULL.
|
||||
* @param error Location to store errors in, or NULL. Ignored for unchecked requests.
|
||||
* @param size The number of bytes expected in the reply. If the reply is too short,
|
||||
* the connection will be shut down. If reply points to a non-NULL pointer on return,
|
||||
* that pointer is guaranteed to point to size bytes of memory, or 32 bytes if size is
|
||||
* less than 32.
|
||||
* @return 1 when the reply to the request was returned, else 0.
|
||||
*
|
||||
* Checks if the reply to the given request already received. Does not block.
|
||||
*
|
||||
* Unlike its xcb_poll_for_reply_safe() counterpart, the given sequence number is not
|
||||
* automatically "widened" to 64-bit.
|
||||
*/
|
||||
int xcb_poll_for_reply64_safe(xcb_connection_t *c, uint64_t request, void **reply, xcb_generic_error_t **error, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Poll for the reply of a given request.
|
||||
* @param c The connection to the X server.
|
||||
* @param request Sequence number of the request as returned by xcb_send_request().
|
||||
* @param reply Location to store the reply in, must not be NULL.
|
||||
* @param error Location to store errors in, or NULL. Ignored for unchecked requests.
|
||||
* @return 1 when the reply to the request was returned, else 0.
|
||||
* @deprecated This function does not perform bounds checks. If reply is not NULL on
|
||||
* return, it is only guaranteed to point to 32 bytes of memory.
|
||||
*
|
||||
* Checks if the reply to the given request already received. Does not block.
|
||||
*/
|
||||
int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply, xcb_generic_error_t **error);
|
||||
|
||||
/**
|
||||
|
@ -282,6 +357,8 @@ int xcb_poll_for_reply(xcb_connection_t *c, unsigned int request, void **reply,
|
|||
* @param reply Location to store the reply in, must not be NULL.
|
||||
* @param error Location to store errors in, or NULL. Ignored for unchecked requests.
|
||||
* @return 1 when the reply to the request was returned, else 0.
|
||||
* @deprecated This function does not perform bounds checks. If reply is not NULL on
|
||||
* return, it is only guaranteed to point to 32 bytes of memory.
|
||||
*
|
||||
* Checks if the reply to the given request already received. Does not block.
|
||||
*
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB Composite Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb xcb-xfixes
|
||||
Libs: -L${libdir} -lxcb-composite
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB Damage Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb xcb-xfixes
|
||||
Libs: -L${libdir} -lxcb-damage
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB Double Buffer Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb
|
||||
Libs: -L${libdir} -lxcb-dbe
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB DPMS Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb
|
||||
Libs: -L${libdir} -lxcb-dpms
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB DRI2 Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb
|
||||
Libs: -L${libdir} -lxcb-dri2
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB DRI3 Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb
|
||||
Libs: -L${libdir} -lxcb-dri3
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB GenericEvent Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb
|
||||
Libs: -L${libdir} -lxcb-ge
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB GLX Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb
|
||||
Libs: -L${libdir} -lxcb-glx
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB Present Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb xcb-randr xcb-xfixes xcb-sync xcb-dri3
|
||||
Libs: -L${libdir} -lxcb-present
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB RandR Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb xcb-render
|
||||
Libs: -L${libdir} -lxcb-randr
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB Record Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb
|
||||
Libs: -L${libdir} -lxcb-record
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB Render Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb
|
||||
Libs: -L${libdir} -lxcb-render
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB X-Resource Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb
|
||||
Libs: -L${libdir} -lxcb-res
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB Screensaver Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb
|
||||
Libs: -L${libdir} -lxcb-screensaver
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB Shape Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb
|
||||
Libs: -L${libdir} -lxcb-shape
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB Shm Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb
|
||||
Libs: -L${libdir} -lxcb-shm
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB Sync Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb
|
||||
Libs: -L${libdir} -lxcb-sync
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB Xevie Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb
|
||||
Libs: -L${libdir} -lxcb-xevie
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB XFree86-DRI Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb
|
||||
Libs: -L${libdir} -lxcb-xf86dri
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB XFixes Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb xcb-render xcb-shape
|
||||
Libs: -L${libdir} -lxcb-xfixes
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB Xinerama Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb
|
||||
Libs: -L${libdir} -lxcb-xinerama
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB XInput Extension (EXPERIMENTAL)
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb xcb-xfixes
|
||||
Libs: -L${libdir} -lxcb-xinput
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB Keyboard Extension (EXPERIMENTAL)
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb
|
||||
Libs: -L${libdir} -lxcb-xkb
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB Xprint Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb
|
||||
Libs: -L${libdir} -lxcb-xprint
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB SELinux Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb
|
||||
Libs: -L${libdir} -lxcb-xselinux
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB XTEST Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb
|
||||
Libs: -L${libdir} -lxcb-xtest
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB Xv Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb xcb-shm
|
||||
Libs: -L${libdir} -lxcb-xv
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
|
@ -8,4 +8,4 @@ Description: XCB XvMC Extension
|
|||
Version: @PACKAGE_VERSION@
|
||||
Requires.private: xcb xcb-xv
|
||||
Libs: -L${libdir} -lxcb-xvmc
|
||||
Cflags: -I${includedir}
|
||||
Cflags: -I${includedir} @XCB_CFLAGS@
|
||||
|
|
Loading…
Reference in New Issue