Contraintes

Un fichier .dex est le format de transport du bytecode Dalvik. Pour qu'un fichier soit un fichier .dex valide, certaines contraintes syntaxiques et sémantiques doivent être respectées. De plus, un environnement d'exécution est nécessaire pour n'accepter que les fichiers .dex valides.

Contraintes générales d'intégrité des fichiers .dex

Les contraintes d'intégrité générales concernent la structure globale d'un fichier .dex, comme décrit en détail dans le format .dex.

Identifiant Description
G1 Le numéro magic du fichier .dex doit être dex\n035\0 pour la version 35 ou similaire pour les versions ultérieures.
O2 La somme de contrôle doit être une somme de contrôle Adler-32 de l'intégralité du contenu du fichier, à l'exception des champs magic et checksum.
G3 La signature doit être un hachage SHA-1 de l'intégralité du contenu du fichier, à l'exception de magic, checksum et signature.
G4

file_size doit correspondre à la taille réelle du fichier, en octets. (version 40 ou antérieure)

file_size doit pointer vers l'en-tête suivant du conteneur ou à la fin du fichier physique (le conteneur). S'il pointe vers l'en-tête suivant, la taille du fichier doit être alignée sur 4 octets. La somme de tous les champs file_size doit être égale à container_size. (version 41 ou ultérieure)

G5

header_size doit avoir la valeur 0x70. (version 40 ou antérieure)

header_size doit avoir la valeur: 0x78 (version 41 ou ultérieure)

G6 endian_tag doit avoir la valeur ENDIAN_CONSTANT ou REVERSE_ENDIAN_CONSTANT.
G7

Pour chacune des sections link, string_ids, type_ids, proto_ids, field_ids, method_ids, class_defs et data, les champs offset et size doivent être tous deux nuls ou non nuls. Dans ce dernier cas, le décalage doit être aligné sur quatre octets.

Les champs offset et size doivent se trouver dans le conteneur et faire référence aux données situées après l'en-tête qui les définit. (version 41 ou ultérieure)

G8 Tous les champs de décalage de l'en-tête, à l'exception de map_off, doivent être alignés sur quatre octets.
G9 Le champ map_off doit être nul ou pointer vers la section de données. Dans ce dernier cas, la section data doit exister.
G10 Aucune des sections link, string_ids, type_ids, proto_ids, field_ids, method_ids, class_defs et data ne doit se chevaucher ni se superposer à l'en-tête.
G11 Si une carte existe, chaque entrée de carte doit avoir un type valide. Chaque type ne peut apparaître qu'une seule fois.
G12 Si une carte existe, chaque entrée de carte doit avoir un décalage et une taille non nuls. Le décalage doit pointer vers la section correspondante du fichier (c'est-à-dire qu'un string_id_item doit pointer vers la section string_ids), et la taille explicite ou implicite de l'élément doit correspondre au contenu et à la taille réels de la section.
G13 Si une carte existe, le décalage de l'entrée de carte n+1 doit être supérieur ou égal au décalage de l'entrée de carte n plus than size of map entry n. Cela implique des entrées non superposées et un ordre de bas en haut.
G14 Les types d'entrées suivants doivent avoir un décalage aligné sur quatre octets: string_id_item, type_id_item, proto_id_item, field_id_item, method_id_item, class_def_item, type_list, code_item et annotations_directory_item.
G15

Pour chaque string_id_item, le champ string_data_off doit contenir une référence valide dans la section data. (version 40 ou antérieure)

Pour chaque string_id_item, le champ string_data_off doit être un décalage dans le conteneur et après tout en-tête qui l'utilise de manière transitoire. (version 41 ou ultérieure)

Pour le string_data_item référencé, le champ data doit contenir une chaîne MUTF-8 valide, et le utf16_size doit correspondre à la longueur décodée de la chaîne.

G16 Pour chaque type_id_item, le champ descriptor_idx doit contenir une référence valide dans la liste string_ids. La chaîne référencée doit être un descripteur de type valide.
G17 Pour chaque proto_id_item, le champ shorty_idx doit contenir une référence valide dans la liste string_ids. La chaîne référencée doit être un descripteur de raccourci valide. De plus, le champ return_type_idx doit être un indice valide dans la section type_ids, et le champ parameters_off doit être nul ou un décalage valide pointant vers la section data. Si la valeur n'est pas nulle, la liste des paramètres ne doit pas contenir d'entrées vides.
G18 Pour chaque field_id_item, les champs class_idx et type_idx doivent être des indices valides dans la liste type_ids. L'entrée référencée par class_idx doit être un type de référence autre qu'un tableau. En outre, le champ name_idx doit être une référence valide dans la section string_ids, et le contenu de l'entrée référencée doit être conforme à la spécification MemberName.
G19 Pour chaque method_id_item, le champ class_idx doit être un indice valide dans la section type_ids, et l'entrée référencée doit être un type de référence autre qu'un tableau. Le champ proto_id doit être une référence valide dans la liste proto_ids. Le champ name_idx doit être une référence valide dans la section string_ids, et le contenu de l'entrée référencée doit être conforme à la spécification MemberName.
G20 Pour chaque field_id_item, le champ class_idx doit être un indice valide dans la liste type_ids. L'entrée référencée doit être un type de référence autre qu'un tableau.

Contraintes de bytecode statiques

Les contraintes statiques sont des contraintes appliquées à des éléments individuels du bytecode. Elles peuvent généralement être vérifiées sans utiliser de techniques de contrôle ou d'analyse du flux de données.

Identifiant Description
A1 Le tableau insns ne doit pas être vide.
A2 Le premier opcode du tableau insns doit avoir l'index 0.
A3 Le tableau insns ne doit contenir que des codes d'instruction Dalvik valides.
A4 L'index de l'instruction n+1 doit être égal à l'index de l'instruction n plus la longueur de l'instruction n, en tenant compte des opérandes possibles.
A5 La dernière instruction du tableau insns doit se terminer à l'index insns_size-1.
A6 Toutes les cibles goto et if-<kind> doivent être des opcodes de la même méthode.
A7 Toutes les cibles d'une instruction packed-switch doivent être des opcodes de la même méthode. La taille et la liste des cibles doivent être cohérentes.
A8 Toutes les cibles d'une instruction sparse-switch doivent être des opcodes de la même méthode. Le tableau correspondant doit être cohérent et trié de la valeur la plus basse à la plus élevée.
A9 L'opérande B des instructions const-string et const-string/jumbo doit être un indice valide dans le pool de constantes de chaîne.
A10 L'opérande C des instructions iget<kind> et iput<kind> doit être un indice valide dans le pool de constantes de champ. L'entrée référencée doit représenter un champ d'instance.
A11 L'opérande C des instructions sget<kind> et sput<kind> doit être un indice valide dans le pool de constantes de champ. L'entrée référencée doit représenter un champ statique.
A12 L'opérande C des instructions invoke-virtual, invoke-super, invoke-direct et invoke-static doit être un indice valide dans le pool de constantes de méthode.
A13 L'opérande B des instructions invoke-virtual/range, invoke-super/range, invoke-direct/range et invoke-static/range doit être un indice valide dans le pool de constantes de méthode.
A14 Une méthode dont le nom commence par un "<" ne doit être appelée implicitement que par la VM, et non par du code provenant d'un fichier .dex. La seule exception est l'initialiseur d'instance, qui peut être appelé par invoke-direct.
A15 L'opérande C de l'instruction invoke-interface doit être un indice valide dans le pool de constantes de méthode. Le method_id référencé doit appartenir à une interface (et non à une classe).
A16 L'opérande B de l'instruction invoke-interface/range doit être un indice valide dans le pool de constantes de méthode. Le method_id référencé doit appartenir à une interface (et non à une classe).
A17 L'opérande B des instructions const-class, check-cast, new-instance et filled-new-array/range doit être un indice valide dans le pool de constantes de type.
A18 L'opérande C des instructions instance-of, new-array et filled-new-array doit être un indice valide dans le pool de constantes de type.
A19 Les dimensions d'un tableau créé par une instruction new-array doivent être inférieures à 256.
A20 L'instruction new ne doit pas faire référence à des classes de tableaux, à des interfaces ni à des classes abstraites.
A21 Le type référencé par une instruction new-array doit être un type valide sans référence.
A22 Tous les registres référencés par une instruction de largeur unique (non paire) doivent être valides pour la méthode en cours. Autrement dit, leurs indices doivent être non négatifs et inférieurs à registers_size.
A23 Tous les registres référencés par une instruction en double largeur (paire) doivent être valides pour la méthode actuelle. Autrement dit, leurs indices doivent être non négatifs et inférieurs à registers_size-1.
A24 L'opérande method_id des instructions invoke-virtual et invoke-direct doit appartenir à une classe (et non à une interface). Dans les fichiers Dex antérieurs à la version 037, il doit en être de même pour les instructions invoke-super et invoke-static.
A25 L'opérande method_id des instructions invoke-virtual/range et invoke-direct/range doit appartenir à une classe (et non à une interface). Dans les fichiers Dex antérieurs à la version 037, il doit en être de même pour les instructions invoke-super/range et invoke-static/range.

Contraintes de bytecode structurelles

Les contraintes structurelles sont des contraintes sur les relations entre plusieurs éléments du bytecode. Elles ne peuvent généralement pas être vérifiées sans utiliser des techniques de contrôle ou d'analyse du flux de données.

Identifiant Description
B1 Le nombre et les types d'arguments (registres et valeurs immédiates) doivent toujours correspondre à l'instruction.
B2 Les paires de registres ne doivent jamais être séparées.
B3 Un registre (ou une paire) doit d'abord être attribué avant de pouvoir être lu.
B4 Une instruction invoke-direct ne doit appeler un initialiseur d'instance ou une méthode que dans la classe actuelle ou l'une de ses super-classes.
B5 Un initialiseur d'instance ne doit être appelé que sur une instance non initialisée.
B6 Les méthodes d'instance ne peuvent être appelées que sur des instances déjà initialisées et les champs d'instance ne peuvent être accessibles que sur des instances déjà initialisées.
B7 Un registre contenant le résultat d'une instruction new-instance ne doit pas être utilisé si la même instruction new-instance est à nouveau exécutée avant l'initialisation de l'instance.
B8 Un initialiseur d'instance doit appeler un autre initialiseur d'instance (même classe ou super-classe) avant que les membres de l'instance puissent être accessibles. Les exceptions sont des champs d'instance non hérités, qui peuvent être attribués avant d'appeler un autre initialiseur et la classe Object en général.
B9 Tous les arguments de méthode réels doivent être compatibles avec leurs arguments formels respectifs.
B10 Pour chaque appel de méthode d'instance, l'instance réelle doit être compatible avec l'affectation de la classe ou de l'interface spécifiée dans l'instruction.
B11 Une instruction return<kind> doit correspondre au type renvoyé de sa méthode.
B12 Lorsque vous accédez aux membres protégés d'une super-classe, le type réel de l'instance à laquelle vous accédez doit être la classe actuelle ou l'une de ses sous-classes.
B13 Le type d'une valeur stockée dans un champ statique doit être compatible avec l'affectation ou convertible en type de champ.
B14 Le type d'une valeur stockée dans un champ doit être compatible avec le type du champ ou convertible en celui-ci.
B15 Le type de chaque valeur stockée dans un tableau doit être compatible avec l'affectation du type de composant du tableau.
B16 L'opérande A d'une instruction throw doit être compatible avec l'affectation java.lang.Throwable.
B17 La dernière instruction accessible d'une méthode doit être une instruction goto ou une branche en arrière, une instruction return ou une instruction throw. Il ne doit pas être possible de laisser le tableau insns en bas.
B18 La moitié non attribuée d'une ancienne paire de registres ne peut pas être lue (elle est considérée comme non valide) tant qu'elle n'a pas été réattribuée par une autre instruction.
B19 Une instruction move-result<kind> doit être immédiatement précédée (dans le tableau insns) par une instruction invoke-<kind>. La seule exception est l'instruction move-result-object, qui peut également être précédée d'une instruction filled-new-array.
B20 Une instruction move-result<kind> doit être immédiatement précédée (dans le flux de contrôle réel) par une instruction return-<kind> correspondante (elle ne doit pas être ignorée). La seule exception est l'instruction move-result-object, qui peut également être précédée d'une instruction filled-new-array.
B21 Une instruction move-exception ne doit apparaître que comme première instruction dans un gestionnaire d'exception.
B22 Les pseudo-instructions packed-switch-data, sparse-switch-data et fill-array-data ne doivent pas être accessibles par le flux de contrôle.