| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <lib/fidl/coding.h> |
| |
| zx_status_t fidl_validate_string(const char* data, uint64_t size) { |
| if (!data) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (size > FIDL_MAX_SIZE) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| uint64_t pos = 0; |
| uint64_t next_pos = 0; |
| uint32_t code_point = 0; |
| while (pos < size) { |
| // The following comparison relies on treating the byte as if it was an |
| // unsigned 8-bit value. However, both signed and unsigned char are allowed |
| // in the C++ spec, with x64 choosing signed, and arm64 choosing unsigned. |
| // We therefore force the byte to be treated as unsigned, since we cannot |
| // rely on the default. |
| unsigned char byte = data[pos]; |
| if (byte < 0b10000000) { |
| pos++; |
| continue; |
| } else if ((byte & 0b11100000) == 0b11000000) { |
| next_pos = pos + 2; |
| if (next_pos > size) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if ((data[pos + 1] & 0b11000000) != 0b10000000) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| // range check |
| code_point = (byte & 0b00011111) << 6 | |
| (data[pos + 1] & 0b00111111); |
| if (code_point < 0x80 || 0x7ff < code_point) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| } else if ((byte & 0b11110000) == 0b11100000) { |
| next_pos = pos + 3; |
| if (next_pos > size) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if ((data[pos + 1] & 0b11000000) != 0b10000000) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if ((data[pos + 2] & 0b11000000) != 0b10000000) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| // range check |
| code_point = (byte & 0b00001111) << 12 | |
| (data[pos + 1] & 0b00111111) << 6 | |
| (data[pos + 2] & 0b00111111); |
| if (code_point < 0x800 || 0xffff < code_point || |
| (0xd7ff < code_point && code_point < 0xe000)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| } else { // 0b11110000 |
| next_pos = pos + 4; |
| if (next_pos > size) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if ((data[pos + 1] & 0b11000000) != 0b10000000) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if ((data[pos + 2] & 0b11000000) != 0b10000000) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if ((data[pos + 3] & 0b11000000) != 0b10000000) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| // range check |
| code_point = (byte & 0b00000111) << 18 | |
| (data[pos + 1] & 0b00111111) << 12 | |
| (data[pos + 2] & 0b00111111) << 6 | |
| (data[pos + 3] & 0b00111111); |
| if (code_point < 0xffff || 0x10ffff < code_point) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| } |
| pos = next_pos; |
| } |
| return ZX_OK; |
| } |