KasperskyOS Community Edition 1.2

IDL description

IDL descriptions are placed into separate *.idl files and contain declarations in the Interface Definition Language (IDL):

  1. Package name. The following declaration is used:
    package <package name>
  2. [Optional] Packages from which the data types for interface method parameters are imported. The following declaration is used:
    import <package name>
  3. [Optional] Definitions of data types for parameters of interface methods.
  4. [Optional] Signatures of interface methods. The following declaration is used:
    interface { <interface method name([parameters])>; [...] }

    Each method signature is indicated in a separate line. The method name must not contain any underscores _. Each method in the list has a unique name. The parameters of methods are divided into input parameters (in), output parameters (out), and parameters for transmitting error information (error). The order of parameters in the description is important: first input parameters, then output parameters, then error parameters. Methods of the security interface cannot have output parameters and error parameters.

    Input parameters and output parameters are transmitted in IPC requests and IPC responses, respectively. Error parameters are transmitted in IPC responses if the server cannot correctly handle the corresponding IPC requests.

    The server can inform a client about IPC request processing errors via error parameters as well as through output parameters of interface methods. If the server sets the error flag in an IPC response when an error occurs, this IPC response will contain the error parameters without any output parameters. Otherwise this IPC response will contain output parameters just like when requests are correctly processed. (The error flag is set in IPC responses by using the nk_err_reset() macro defined in the nk/types.h header file from the KasperskyOS SDK.)

    An IPC response sent with the error flag set and an IPC response with the error flag not set are considered to be different types of events for the Kaspersky Security Module. When describing a solution security policy, this difference lets you conveniently distinguish between the processing of events associated with the correct execution of IPC requests and the processing of events associated with incorrect execution of IPC requests. If the server does not set the error flag in IPC responses, the security module must check the values of output parameters indicating errors to properly process events related to the incorrect execution of IPC requests. (A client can check the state of the error flag in an IPC response even if the corresponding interface method does not contain error parameters. To do so, the client uses the nk_msg_check_err() macro defined in the nk/types.h header file from the KasperskyOS SDK.)

    Signatures of interface methods cannot be imported from other IDL files.

The IDL language is case sensitive.

Single-line comments and multi-line comments can be used in an IDL description.

At least one optional declaration is used in a IDL description. If an IDL description does not use at least one optional declaration, this description will correspond to an "empty" package that does not assign any interface methods or data types (including from other IDL descriptions).

Some IDL files from the KasperskyOS SDK do not describe interface methods, but instead only contain definitions of data types. These IDL files are used only as exporters of data types.

If a package contains a description of interface methods, the interface name matches the package name.

Examples of IDL files

Env.idl

package kl.Env // Definitions of data types for interface method parameters typedef string<128> Name; typedef string<256> Arg; typedef sequence<Arg,256> Args; // Interface includes one method. interface { Read(in Name name, out Args args, out Args envs); }

Kpm.idl

package kl.Kpm // Import data types for parameters of interface methods import kl.core.Types // Definition of data type for parameters of interface methods typedef string<64> String; /* Interface includes multiple methods. * Some methods do not have any parameters. */ interface { Shutdown(); Reboot(); PowerButtonPressedWait(); TerminationSignalWait(in UInt32 entityId, in String entityName); EntityTerminated(in UInt32 entityId); Terminate(in UInt32 callingEntityId); }

MessageBusSubs.idl

package kl.MessageBusSubs // Import data types for interface method parameters import kl.MessageBusTypes /* Interface includes a method that has * input and output parameters, and * an error parameter.*/ interface { Wait(in ClientId id, out Message topic, out BundleId dataId, error ResultCode result); }

WaylandTypes.idl

// Package contains only definitions of data types. package kl.WaylandTypes typedef UInt32 ClientId; typedef bytes<8192> Buffer; typedef string<4096> ConnectionId; typedef SInt32 SsizeT; typedef UInt32 SizeT; typedef SInt32 ShmFd; typedef SInt32 ShmId; typedef bytes<16384000> ShmBuffer;

In this section

IDL data types

Integer expressions in IDL

Page top
[Topic ice_idl]

IDL data types

IDL supports primitive data types as well as composite data types. The set of supported composite types includes unions, structures, arrays, and sequences.

Primitive types

IDL supports the following primitive types:

  • SInt8, SInt16, SInt32, SInt64 – signed integer.
  • UInt8, UInt16, UInt32, UInt64 – unsigned integer.
  • Handle – value whose binary representation consists of multiple fields, including a handle field and a handle permissions mask field.
  • bytes<<size in bytes>> – byte buffer consisting of a memory area with a size that does not exceed the defined number of bytes.
  • string<<size in bytes>> – string buffer consisting of a byte buffer whose last byte is a terminating zero. The maximum size of a string buffer is a unit larger than the defined size due to the additional byte with the terminating zero.

Integer literals can be specified in decimal format, hexadecimal format (for example, 0x2f, 0X2f, 0x2F, 0X2F) or octal format (for example, 0O123, 0o123).

You can use the reserved word const to define the named integer constants by assigning their values using integer literals or integer expressions.

Example definitions of named integer constants:

const UInt32 DeviceNameMax = 0o100; const UInt32 HandleTypeUserLast = 0x0001FFFF; const UInt32 MaxLogMessageSize = (2 << 3) ** 2; const UInt32 MaxLogMessageCount = 100; const UInt64 MaxLen = (MaxLogMessageSize + 4) * MaxLogMessageCount;

Named integer constants can be used to avoid problems associated with so-called "magic numbers". For example, if an IDL description defines named integer constants for return codes of an interface method, you can interpret these codes without additional information when describing a policy. Named integer constants and integer expressions can also be applied in definitions of byte buffers, string buffers, and composite types to define the size of data or the number of data elements.

The bytes<<size in bytes>> and string<<size in bytes>> constructs are used in definitions of composite types, signatures of interface methods, and when creating type aliases because they define anonymous types (types without a name).

Unions

A union stores different types of data in one memory area. In an IPC message, a union is provided with an additional tag field that defines which specific member of the union is used.

The following construct is used to define a union:

union <type name> { <member type> <member name>; [...] }

Example of a union definition:

union ExitInfo { UInt32 code; ExceptionInfo exc; }

Structures

The following construct is used to define a structure:

struct <type name> { <field type> <field name>; [...] }

Example of a structure definition:

struct SessionEvqParams { UInt32 count; UInt32 align; UInt32 size; }

Arrays

The following construct is used to define an array:

array<<type of elements, number of elements>>

This construct is used in definitions of other composite types, signatures of interface methods, and when creating type aliases because it defines an anonymous type.

The Handle type can be used as the type of array elements if this array is not included in another composite data type. However, the total number of handles in an IPC message cannot exceed 255.

Sequences

A sequence is a variable-sized array. When defining a sequence, the maximum number of elements of the sequence is specified.

The following construct is used to define a sequence:

sequence<<type of elements, number of elements>>

This construct is used in definitions of other composite types, signatures of interface methods, and when creating type aliases because it defines an anonymous type.

The Handle type cannot be used as the type of sequence elements.

Variable-size and fixed-size types

The bytes, string and sequence types are variable-size types. In other words, the maximum number of elements is assigned when defining these types, but less elements (or none) may actually be used. Data of the bytes, string and sequence types are stored in the IPC message arena. All other types are fixed-size types. Data of fixed-size types are stored in the constant part of IPC messages.

Types based on composite types

Composite types can be used to define other composite types. The definition of an array or sequence can also be included in the definition of another type.

Example definition of a structure with embedded definitions of an array and sequence:

const UInt32 MessageSize = 64; struct BazInfo { array<UInt8, 100> a; sequence<sequence<UInt32, MessageSize>, ((2 << 2) + 2 ** 2) * MessageSize> b; string<100> c; bytes<4096> d; UInt64 e; }

The definition of a union or structure cannot be included in the definition of another type. However, a type definition may include already defined unions and structures. This is done by indicating the names of the included types in the type definition.

Example definition of a structure that includes a union and structure:

union foo { UInt32 value1; UInt8 value2; } struct bar { UInt32 a; UInt8 b; } struct BazInfo { foo x; bar y; }

Creating aliases of types

Type aliases make it more convenient to work with types. For example, type aliases can be used to assign mnemonic names to types that have abstract names. Assigned aliases for anonymous types also let you receive named types.

The following construct is used to create a type alias:

typedef <type name/anonymous type definition> <type alias>

Example of creating mnemonic aliases:

typedef UInt64 ApplicationId; typedef Handle PortHandle;

Example of creating an alias for an array definition:

typedef array<UInt8, 4> IP4;

Example of creating an alias for a sequence definition:

const UInt32 MaxDevices = 8; struct Device { string<32> DeviceName; UInt8 DeviceID; } typedef sequence<Device, MaxDevices> Devices;

Example of creating an alias for a union definition:

union foo { UInt32 value1; UInt8 value2; } typedef foo bar;

Defining anonymous types in signatures of interface methods

Anonymous types can be defined in signatures of interface methods.

Example of defining a sequence in an interface method signature:

const UInt8 DeviceCount = 8; interface { Poll(in UInt32 timeout, out sequence<UInt32, DeviceCount / 2> report, out UInt32 count, out UInt32 rc); }
Page top
[Topic ice_idl_data_types]

Integer expressions in IDL

Integer expressions in IDL are composed of named integer constants, integer literals, operators (see the table below), and grouping parentheses.

Example use of integer expressions:

const UInt8 itemHeaderLen = 2; const UInt8 itemBlockLen = 4; const UInt8 maxItemCount = 0X10; const UInt64 maxLen = (2 << 3) + (itemHeaderLen + itemBlockLen * 4) * maxItemCount; interface { CopyPage(in array<UInt8, 4 * maxLen> page); }

If an integer overflow occurs when computing an expression, the source code generator using the IDL file will terminate with an error.

Details on operators of integer expressions in IDL

Syntax

Operation

Precedence

Associativity

Special considerations

-a

Sign change

1

No

N/A

~a

Bitwise negation

1

No

N/A

a ** b

Exponentiation

2

No

Special considerations:

  • Due to the lack of associativity, you must use parentheses when specifying multiple consecutive operators to define the order of operations.

    Example:

    (a ** b) ** c

    a ** (b ** c)

  • The power exponent cannot be negative.

a * b

Multiplication

3

Left

N/A

a / b

Integer division

3

Left

Special considerations:

  • The result of the operation is a value obtained by rounding the real quotient down to the next lower integer. For example, 4 / 3 = 1, -4 / 3 = -2.
  • The divisor cannot be zero.

a % b

Modulo

3

Left

Special considerations:

  • The sign of the operation result matches the sign of the divisor. For example, -5 % 2 = 1, 5 % -2 = -1.
  • The divisor cannot be zero.

a + b

Addition

4

Left

N/A

a - b

Subtraction

4

Left

N/A

a << b

Bit shift left

2*

No

Special considerations:

  • The precedence is lower than with unary operations but incomparable to the precedences of binary arithmetic operations. Therefore, in expressions with binary arithmetic operations, you must use parentheses to define the order of operations.

    Example:

    a << (b + c)

    (a << b) ** c

  • Due to the lack of associativity, you must use parentheses when specifying multiple consecutive operators to define the order of operations.

    Example:

    (a << b) << c

    a << (b << c)

  • The shift value must be within the interval [0; 63].

a >> b

Bit shift right

2*

No

Special considerations:

  • The precedence is lower than with unary operations but incomparable to the precedences of binary arithmetic operations. Therefore, in expressions with binary arithmetic operations, you must use parentheses to define the order of operations.

    Example:

    a >> (b * c)

    (a >> b) / c

  • Due to the lack of associativity, you must use parentheses when specifying multiple consecutive operators to define the order of operations.

    Example:

    (a >> b) >> c

    a >> (b >> c)

  • The shift value must be within the interval [0; 63].

Page top
[Topic ice_idl_int_expres]