Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch Statement Segfault on ARM #44563

Closed
wm17 opened this issue Dec 28, 2020 · 1 comment
Closed

Switch Statement Segfault on ARM #44563

wm17 opened this issue Dec 28, 2020 · 1 comment
Assignees
Labels
area-vm Use area-vm for VM related issues, including code coverage, FFI, and the AOT and JIT backends. crash Process exits with SIGSEGV, SIGABRT, etc. An unhandled exception is not a crash. P1 A high priority bug; for example, a single project is unusable or has many test failures

Comments

@wm17
Copy link

wm17 commented Dec 28, 2020

The code below causes a segfault for us on Android/ios arm devices. We encountered this when running a release build of a Flutter app on an arm Android device (built on Windows). We first noticed it in Flutter 1.22.0 / Dart 2.10.1. Issue did not occur when tested with Flutter 1.20.4 / Dart 2.9.2.

A minimal Flutter app that reproduces the issue can also be seen here.

Code

import 'dart:typed_data';

class BaseClass {
  BaseClass({this.data});
  final ByteData data;
  int get value => null;
}

class Class1 extends BaseClass {
  static const int VALUE = 0x01;
  Class1({ByteData data}) : super(data: data);
  @override int get value => VALUE;
}

class Class2 extends BaseClass {
  static const int VALUE = 0x02;
  Class2({ByteData data}) : super(data: data);
  @override int get value => VALUE;
}

class Class3 extends BaseClass {
  static const int VALUE = 0x03;
  Class3({ByteData data}) : super(data: data);
  @override int get value => VALUE;
}

class Class4 extends BaseClass {
  static const int VALUE = 0x04;
  Class4({ByteData data}) : super(data: data);
  @override int get value => VALUE;
}

class ClassResolver {
  ClassResolver._();

  static BaseClass resolveClass(int value, ByteData data) {
    var klass = BaseClass(data: data);
    switch(value) {
      case 0x100001: print("A"); klass = Class1(data: data); break;
      case 0x100000: print("B"); klass = Class2(data: data); break;
      case 0x110001: print("C"); klass = Class3(data: data); break;
      case 0x110000: print("D"); klass = Class4(data: data); break;
      default: throw Exception("Failed to resolve class.");
    }

    print("Z");
    print(klass.value); // <-- SEGV_MAPERR at this line
    return klass;
  }
}

void main() {
  var data = Uint8List.fromList([0, 1, 2, 3, 4]).buffer.asByteData();
  ClassResolver.resolveClass(0x100001, data);
  ClassResolver.resolveClass(0x100000, data);
  ClassResolver.resolveClass(0x110001, data);
  ClassResolver.resolveClass(0x110000, data);
}

Segfault on Pixel 3

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'google/blueline/blueline:11/RP1A.201005.004/6782484:user/release-keys'
Revision: 'MP1.0'
ABI: 'arm64'
Timestamp: 2020-12-28 15:03:57-0700
pid: 24535, tid: 24564, name: 1.ui  >>> com.example.switch_segfault <<<
uid: 10636
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x2
Cause: null pointer dereference
    x0  0000000000000001  x1  0000000000000034  x2  0000000000000000  x3  0000000000000000
    x4  0000000000000010  x5  8080808080808080  x6  fefefefefefefeff  x7  7f7f7f7f7f7f7f7f
    x8  0000000000000000  x9  0000007190621b70  x10 0000000000000400  x11 0000000000000000
    x12 0000006fe4bb5cd0  x13 0000000000000002  x14 0000182286a29c7a  x15 0000006fe4bb77a8
    x16 0000006fe4adf000  x17 00000072e0e57ad4  x18 0000006fe400e000  x19 000000719061cc20
    x20 0000000000000000  x21 000000725060b020  x22 0000006fd7e00041  x23 0000007180614db0
    x24 0000006fd7e07ee1  x25 0000006fe4ade000  x26 000000719061cc20  x27 0000006fd668a8c0
    x28 0000000000000004  x29 0000006fe4bb77a8
    lr  0000006fe1855d84  sp  0000006fe4ade000  pc  0000006fe18136e4  pst 0000000020000000
backtrace:
      #00 pc 00000000000206e4  /data/app/~~uEWGQ46I0o6695usGt5xFw==/com.example.switch_segfault-nLTC1ITFA68USrUhj7dTmg==/lib/arm64/libapp.so (offset 0xf000) (_kDartIsolateSnapshotInstructions+71396) (BuildId: 95227cfd47606f7d9553fa125279094e)
@srawlins srawlins added the area-vm Use area-vm for VM related issues, including code coverage, FFI, and the AOT and JIT backends. label Dec 29, 2020
@mkustermann mkustermann added crash Process exits with SIGSEGV, SIGABRT, etc. An unhandled exception is not a crash. P1 A high priority bug; for example, a single project is unusable or has many test failures labels Dec 31, 2020
@alexmarkov
Copy link
Contributor

@wm17 Thank you for the detailed bug report!

The problem is that unboxing information was not attached to BaseClass.value because its body is unreachable.
So compiler treated result of dispatch table call to BaseClass.value as boxed, while all its implementations in sub-classes actually return unboxed value:

  class BaseClass extends core::Object {
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:1] [@vm.unreachable.metadata=]    get value() → core::int*
      throw "Attempt to execute method removed by Dart AOT compiler (TFA)";
  }
  class Class1 extends tes::BaseClass {
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:1] [@vm.unboxing-info.metadata=()->i]    @#C1
    get value() → core::int*
      return #C2;
  }

Here is a smaller repro:

class BaseClass {
  int get value => 0;
}

class Class1 extends BaseClass {
  @pragma('vm:never-inline')
  int get value => 1;
}

class Class2 extends BaseClass {
  @pragma('vm:never-inline')
  int get value => 2;
}

bool nonConstantCondition = int.parse("1") == 1;

void main() {
  BaseClass obj = BaseClass();
  obj = nonConstantCondition ? Class1() : Class2();
  print(obj.value);
}

https://dart-review.googlesource.com/c/sdk/+/177621 should fix this bug.

dart-bot pushed a commit that referenced this issue Jan 12, 2021
Unreachable members could be used as interface targets of dispatch
table calls, so they should have correct unboxing metadata.
This change fixes attaching unboxing info to such members.

TEST=runtime/tests/vm/dart/regress_44563_test.dart

Fixes #44563

Change-Id: I5da6a8d07048904eb94b05bfba11bdf72d655e12
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/177621
Reviewed-by: Aske Simon Christensen <askesc@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
dart-bot pushed a commit that referenced this issue Jan 19, 2021
Unreachable members could be used as interface targets of dispatch
table calls, so they should have correct unboxing metadata.
This change fixes attaching unboxing info to such members.

TEST=runtime/tests/vm/dart/regress_44563_test.dart

Fixes #44563

Change-Id: I5da6a8d07048904eb94b05bfba11bdf72d655e12
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/177621
Reviewed-by: Aske Simon Christensen <askesc@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-vm Use area-vm for VM related issues, including code coverage, FFI, and the AOT and JIT backends. crash Process exits with SIGSEGV, SIGABRT, etc. An unhandled exception is not a crash. P1 A high priority bug; for example, a single project is unusable or has many test failures
Projects
None yet
Development

No branches or pull requests

4 participants