runner-images icon indicating copy to clipboard operation
runner-images copied to clipboard

MTLResource newBufferWithBytesNoCopy:length:options:deallocator: failed to create on mac os runner

Open mesozoic-egg opened this issue 1 year ago • 4 comments

Description

On Mac OS runner, newBufferWithBytesNoCopy:length:options:deallocator: create an instance with length zero and accesing elements causes a segmentation fault

    id<MTLBuffer> device_buffer = [device newBufferWithBytesNoCopy:ns_mutable_ptr
                                                            length:length
                                                           options:0
                                                       deallocator:nil
    ];
    printf("Device buffer created\n");
    int device_buffer_length = [device_buffer length];
    printf("Length: %d\n", device_buffer_length);

view full

Platforms affected

  • [ ] Azure DevOps
  • [x] GitHub Actions - Standard Runners
  • [ ] GitHub Actions - Larger Runners

Runner images affected

  • [ ] Ubuntu 20.04
  • [ ] Ubuntu 22.04
  • [ ] Ubuntu 24.04
  • [X] macOS 12
  • [ ] macOS 13
  • [ ] macOS 13 Arm64
  • [X] macOS 14
  • [X] macOS 14 Arm64
  • [ ] Windows Server 2019
  • [ ] Windows Server 2022

Image version and build link

macos-12 macos-14 https://github.com/mesozoic-egg/newbufferwithbytesnocopy/actions/runs/10977075322

Is it regression?

No

Expected behavior

The length should not be zero for the returned MTLBuffer instance

$ clang -framework CoreGraphics -framework Foundation -framework Metal newbufnocopy.m -o newbufnocopy && ./newbufnocopy
Malloc data[0] = 10
Malloc data[1] = 20
Malloc data[2] = 30
Malloc data[3] = 40
NSMutable data created
Length: 16
NSMutable data[0] = 10
NSMutable data[1] = 20
NSMutable data[2] = 30
NSMutable data[3] = 40
Device buffer created
Length: 16
Data pointer obtained
Device buffer[0] = 10
Device buffer[1] = 20
Device buffer[2] = 30
Device buffer[3] = 40

Actual behavior

The length being returned is 0 and accessing the elements cause a segmentation fault, happens on both arm (macos 14) and x86 (macos 12)

$ clang -framework CoreGraphics -framework Foundation -framework Metal newbufnocopy.m -o newbufnocopy
Malloc data[0] = 10
Malloc data[1] = 20
Malloc data[2] = 30
Malloc data[3] = 40
NSMutable data created
Length: 16
NSMutable data[0] = 10
NSMutable data[1] = 20
NSMutable data[2] = 30
NSMutable data[3] = 40
Device buffer created
Length: 0
Data pointer obtained
Device buffer length is 0 <----- THIS WOULD CAUSE SEG FAULT

Repro steps

reproducible example

  1. Compile and run this script with clang -framework CoreGraphics -framework Foundation -framework Metal newbufnocopy.m -o newbufnocopy && ./newbufnocopy

mesozoic-egg avatar Sep 22 '24 01:09 mesozoic-egg

Hi @mesozoic-egg, I will look into this issue and keep you updated.

sureshe456 avatar Sep 23 '24 06:09 sureshe456

When changing to use the vm_allocate, I saw that the mac 14 (arm) runner worked as expected, but not on mac 12 (x86) runner mac 14 arm: https://github.com/mesozoic-egg/newbufferwithbytesnocopy/actions/runs/11055124079/job/30713571130 mac 12 x86: https://github.com/mesozoic-egg/newbufferwithbytesnocopy/actions/runs/11055124079/job/30713571752

code: https://github.com/mesozoic-egg/newbufferwithbytesnocopy/blob/vm_alloc/newbufnocopy.m

    int length = 16;
    void *updateAddress;
    kern_return_t err = vm_allocate((vm_map_t)mach_task_self(),
                                            (vm_address_t*)&updateAddress,
                                            length,
                                            VM_FLAGS_ANYWHERE);
    for (int i = 0; i < 4; i++) {
        ((int*)updateAddress)[i] = i;
    }
    assert(err == KERN_SUCCESS);
    id<MTLBuffer> device_buffer = [device newBufferWithBytesNoCopy:updateAddress
                                                            length:length
                                                           options:0
                                                       deallocator:nil
    ];
    printf("Device buffer created\n");
    int device_buffer_length = [device_buffer length];
    printf("Length: %d\n", device_buffer_length);

mesozoic-egg avatar Sep 26 '24 15:09 mesozoic-egg

Hi @mesozoic-egg, Thanks for sharing the details. will check and get back to you with updates.

sureshe456 avatar Sep 27 '24 15:09 sureshe456

Hi @mesozoic-egg,

  1. Since we are deprecating macOS 12 by December and no longer generating new images for it, we recommend transitioning to macOS 14 or macOS 15, where the issue is not present.
  2. As a workaround, you can use posix_memalign to ensure the allocated memory is aligned to 16 bytes, which is required by Metal. We've tested this across all macOS images, and it works as expected. Please use the following code and test it on macOS 12 and macOS 14 to confirm that the buffer length is correctly displayed as 16 bytes.

Please let us know if this works for you.

/*
 * clang -framework CoreGraphics -framework Foundation -framework Metal newbufnocopy.m -o newbufnocopy && ./newbufnocopy
 */

#import <Foundation/Foundation.h>
#import <Metal/Metal.h>
#import <CoreGraphics/CoreGraphics.h>
#include <stdlib.h>  // For posix_memalign

int main(int argc, char** argv)
{
    id<MTLDevice> device = MTLCreateSystemDefaultDevice();
    int length = 16;
    void *updateAddress;

    // Use posix_memalign to ensure memory alignment
    int alignment = 16;  // Metal usually requires at least 16-byte alignment
    int result = posix_memalign(&updateAddress, alignment, length);
    
    for (int i = 0; i < 4; i++) {
        ((int*)updateAddress)[i] = i;
    }

    // Create a Metal buffer with newBufferWithBytes
    id<MTLBuffer> device_buffer = [device newBufferWithBytes:updateAddress
                                                      length:length
                                                     options:0
    ];
    
    printf("Device buffer created\n");
    int device_buffer_length = (int)[device_buffer length];
    printf("Length: %d\n", device_buffer_length);
    
    int *data = (int*)[device_buffer contents];
    printf("Data pointer obtained\n");

    if (device_buffer_length == 0) {
        printf("Device buffer length is 0\n");
        free(updateAddress);
        return 0;
    }

    for (int i = 0; i < 4; i++) {
        printf("Device buffer[%d] = %d\n", i, data[i]);
    }
    
    free(updateAddress);
    return 0;
}
Screenshot 2024-10-16 at 4 02 09 PM

sureshe456 avatar Oct 16 '24 14:10 sureshe456

Hi @mesozoic-egg We believe that you will be able to generate buffer length is correctly displayed as 16 bytes on macOS 12 and macOS 14 images by using above mentioned code.There is no action item pending from our end hence we are closing the issue.

sureshe456 avatar Oct 25 '24 10:10 sureshe456

Thank you! @sureshe456

mesozoic-egg avatar Oct 25 '24 10:10 mesozoic-egg