Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit a65ef31

Browse files
authored
Document (and assert) that channel names can't contains nulls (#43593)
Fixes flutter/flutter#116652
1 parent feaadf8 commit a65ef31

File tree

2 files changed

+55
-11
lines changed

2 files changed

+55
-11
lines changed

lib/ui/channel_buffers.dart

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,14 @@ class _Channel {
243243
/// Typically these buffers are drained once a callback is set up on
244244
/// the [BinaryMessenger] in the Flutter framework. (See [setListener].)
245245
///
246+
/// ## Channel names
247+
///
248+
/// By convention, channels are normally named with a reverse-DNS prefix, a
249+
/// slash, and then a domain-specific name. For example, `com.example/demo`.
250+
///
251+
/// Channel names cannot contain the U+0000 NULL character, because they
252+
/// are passed through APIs that use null-terminated strings.
253+
///
246254
/// ## Buffer capacity and overflow
247255
///
248256
/// Each channel has a finite buffer capacity and messages will
@@ -326,7 +334,11 @@ class ChannelBuffers {
326334
/// If a message overflows the channel, and the channel has not been
327335
/// configured to expect overflow, then, in debug mode, a message
328336
/// will be printed to the console warning about the overflow.
337+
///
338+
/// Channel names cannot contain the U+0000 NULL character, because they
339+
/// are passed through APIs that use null-terminated strings.
329340
void push(String name, ByteData? data, PlatformMessageResponseCallback callback) {
341+
assert(!name.contains('\u0000'), 'Channel names must not contain U+0000 NULL characters.');
330342
final _Channel channel = _channels.putIfAbsent(name, () => _Channel());
331343
if (channel.push(_StoredMessage(data, callback))) {
332344
_printDebug(
@@ -365,6 +377,7 @@ class ChannelBuffers {
365377
///
366378
/// The draining stops if the listener is removed.
367379
void setListener(String name, ChannelCallback callback) {
380+
assert(!name.contains('\u0000'), 'Channel names must not contain U+0000 NULL characters.');
368381
final _Channel channel = _channels.putIfAbsent(name, () => _Channel());
369382
channel.setListener(callback);
370383
}
@@ -416,8 +429,9 @@ class ChannelBuffers {
416429
/// ## `resize`
417430
///
418431
/// The `resize` method takes as its argument a list with two values, first
419-
/// the channel name (a UTF-8 string less than 254 bytes long), and second the
420-
/// allowed size of the channel buffer (an integer between 0 and 2147483647).
432+
/// the channel name (a UTF-8 string less than 254 bytes long and not
433+
/// containing any null bytes), and second the allowed size of the channel
434+
/// buffer (an integer between 0 and 2147483647).
421435
///
422436
/// Upon receiving the message, the channel's buffer is resized. If necessary,
423437
/// messages are silently discarded to ensure the buffer is no bigger than
@@ -433,8 +447,9 @@ class ChannelBuffers {
433447
/// ## `overflow`
434448
///
435449
/// The `overflow` method takes as its argument a list with two values, first
436-
/// the channel name (a UTF-8 string less than 254 bytes long), and second a
437-
/// boolean which is true if overflow is expected and false if it is not.
450+
/// the channel name (a UTF-8 string less than 254 bytes long and not
451+
/// containing any null bytes), and second a boolean which is true if overflow
452+
/// is expected and false if it is not.
438453
///
439454
/// This sets a flag on the channel in debug mode. In release mode the message
440455
/// is silently ignored. The flag indicates whether overflow is expected on this
@@ -473,6 +488,9 @@ class ChannelBuffers {
473488
}
474489
index += 1;
475490
final String channelName = utf8.decode(bytes.sublist(index, index + channelNameLength));
491+
if (channelName.contains('\u0000')) {
492+
throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (channel name must not contain any null bytes)");
493+
}
476494
index += channelNameLength;
477495
if (bytes[index] != 0x03) { // 3 = value code for uint32
478496
throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (second argument must be an integer in the range 0 to 2147483647)");
@@ -533,6 +551,7 @@ class ChannelBuffers {
533551
void resize(String name, int newSize) {
534552
_Channel? channel = _channels[name];
535553
if (channel == null) {
554+
assert(!name.contains('\u0000'), 'Channel names must not contain U+0000 NULL characters.');
536555
channel = _Channel(newSize);
537556
_channels[name] = channel;
538557
} else {
@@ -556,6 +575,7 @@ class ChannelBuffers {
556575
assert(() {
557576
_Channel? channel = _channels[name];
558577
if (channel == null && allowed) {
578+
assert(!name.contains('\u0000'), 'Channel names must not contain U+0000 NULL characters.');
559579
channel = _Channel();
560580
_channels[name] = channel;
561581
}

testing/dart/channel_buffers_test.dart

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ void _resize(ui.ChannelBuffers buffers, String name, int newSize) {
2222
}
2323

2424
void main() {
25+
bool assertsEnabled = false;
26+
assert(() {
27+
assertsEnabled = true;
28+
return true;
29+
}());
30+
2531
test('push drain', () async {
2632
const String channel = 'foo';
2733
final ByteData data = _makeByteData('bar');
@@ -36,9 +42,9 @@ void main() {
3642
// ignore: deprecated_member_use
3743
await buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) async {
3844
expect(drainedData, equals(data));
39-
assert(!called);
45+
expect(called, isFalse);
4046
drainedCallback(drainedData);
41-
assert(called);
47+
expect(called, isTrue);
4248
});
4349
});
4450

@@ -227,21 +233,21 @@ void main() {
227233
buffers.push('a', three, (ByteData? data) { });
228234
log.add('top');
229235
buffers.setListener('a', (ByteData? data, ui.PlatformMessageResponseCallback callback) {
230-
assert(data != null);
236+
expect(data, isNotNull);
231237
log.add('a1: ${utf8.decode(data!.buffer.asUint8List())}');
232238
});
233239
log.add('-1');
234240
await null;
235241
log.add('-2');
236242
buffers.setListener('a', (ByteData? data, ui.PlatformMessageResponseCallback callback) {
237-
assert(data != null);
243+
expect(data, isNotNull);
238244
log.add('a2: ${utf8.decode(data!.buffer.asUint8List())}');
239245
});
240246
log.add('-3');
241247
await null;
242248
log.add('-4');
243249
buffers.setListener('b', (ByteData? data, ui.PlatformMessageResponseCallback callback) {
244-
assert(data != null);
250+
expect(data, isNotNull);
245251
log.add('b: ${utf8.decode(data!.buffer.asUint8List())}');
246252
});
247253
log.add('-5');
@@ -290,7 +296,7 @@ void main() {
290296
buffers.push('a', three, (ByteData? data) { });
291297
log.add('-1');
292298
buffers.setListener('a', (ByteData? data, ui.PlatformMessageResponseCallback callback) {
293-
assert(data != null);
299+
expect(data, isNotNull);
294300
log.add('a1: ${utf8.decode(data!.buffer.asUint8List())}');
295301
});
296302
await null; // handles one
@@ -299,7 +305,7 @@ void main() {
299305
await null;
300306
log.add('-3');
301307
buffers.setListener('a', (ByteData? data, ui.PlatformMessageResponseCallback callback) {
302-
assert(data != null);
308+
expect(data, isNotNull);
303309
log.add('a2: ${utf8.decode(data!.buffer.asUint8List())}');
304310
});
305311
log.add('-4');
@@ -372,6 +378,24 @@ void main() {
372378
'callback2: true',
373379
]);
374380
});
381+
382+
test('ChannelBufferspush rejects names with nulls', () async {
383+
const String channel = 'foo\u0000bar';
384+
final ByteData blabla = _makeByteData('blabla');
385+
final ui.ChannelBuffers buffers = ui.ChannelBuffers();
386+
try {
387+
buffers.push(channel, blabla, (ByteData? data) { });
388+
fail('did not throw as expected');
389+
} on AssertionError catch (e) {
390+
expect(e.toString(), contains('U+0000 NULL'));
391+
}
392+
try {
393+
buffers.setListener(channel, (ByteData? data, ui.PlatformMessageResponseCallback callback) { });
394+
fail('did not throw as expected');
395+
} on AssertionError catch (e) {
396+
expect(e.toString(), contains('U+0000 NULL'));
397+
}
398+
}, skip: !assertsEnabled);
375399
}
376400

377401
class _TestChannelBuffers extends ui.ChannelBuffers {

0 commit comments

Comments
 (0)