Segfault uploading PNG image to Wt Server from Alamofire

  alamofire, c++, swift, wt

I am currently trying to upload a photo from my iOS device using Alamofire and Swift 5 to a web server running Wt, however I get a segmentation fault when doing so. Here is my Swift code:

static func setProfilePhoto(apiKey: String, imageData: Data, promise: Promise) {
        DispatchQueue.global(qos: .userInitiated).async {
            AF.upload(multipartFormData: { multiData in
                multiData.append(imageData, withName: "image", fileName: "file.png", mimeType: "image/png")
            }, to: "[Image URL redacted]", headers: ["api-key": apiKey]).validate().response { response in
                switch response.result {
                case .success(_):
                    promise.success(nil)
                case .failure(_):
                    promise.failure("(response.response?.statusCode ?? -1): (response.bodyString ?? "")")
                }
            }
        }
    }

Here is my C++ implementation, with the line the segfault happens on commented:

void ApiPost::setPhoto() {
    if (request_.contentType().rfind("multipart/form-data", 0) != 0) {
        response_.out() << "Invalid Content-Type! Required: [multipart/form-data] Given: [" + request_.contentType() + "]";
        response_.setStatus(400);
        return;
    }

    const std::string token = request_.headerValue("api-key");
    std::string photoType = *request_.getParameter("type");

    SessionManager session(pool_);
    Wt::Dbo::Transaction transaction(session);

    try {
        std::string id = DatabaseUtils::verifyAuthToken(session, token);
        if (id.empty()) {
            response_.out() << "Invalid authorization token";
            response_.setStatus(401);
            return;
        }

        Wt::Dbo::ptr<UserSettings> settings = session.find<UserSettings>().where("user_id = ?").bind(id);

        // Delete previous file if it exists
        if (photoType == "profile" && settings->profilePicPath_.length() > 0)
            std::filesystem::remove(settings->profilePicPath_);
        else if (photoType == "background" && settings->bkgrndPicPath_.length() > 0)
            std::filesystem::remove(settings->bkgrndPicPath_);

        Wt::Http::UploadedFile photo = request_.uploadedFiles().begin()->second;
        photo.stealSpoolFile(); // Segfault here
        std::string destPath = Statics::getBasePath() + "/photos/" + id + "/";
        std::filesystem::create_directories(destPath);

        std::fstream file(photo.spoolFileName(), std::ios::in | std::ios::binary);
        destPath += HashUtils::sha256Random() + drodilMime::Detector::detectToExtension(file);
        file.close();
        
        std::filesystem::rename(photo.spoolFileName(), destPath);
        if (photoType == "profile")
            settings.modify()->profilePicPath_ = destPath;
        else
            settings.modify()->bkgrndPicPath_ = destPath;

        std::filesystem::remove(photo.spoolFileName());
    }
    catch (Wt::Dbo::Exception& e) {
        std::cout << e.what() << std::endl;
        response_.out() << e.what();
        response_.setStatus(500);
        return;
    }
    catch (std::exception& e) {
        std::cout << e.what() << std::endl;
        response_.out() << e.what();
        response_.setStatus(500);
        return;
    }
}

Here is the GDB error that I get:

0x0000000000810b82 in Wt::Http::UploadedFile::stealSpoolFile (this=0x7ffff2ebe1a0) at /home/jack/Build/vcpkg/buildtrees/wt/src/699342a1a5-7e5d5ddf44.clean/src/Wt/Http/Request.C:84

I have tried not using stealSpoolFile() but it still segfaulted. I checked the actual temp file Wt creates with the file command and it gave back a type of data, not PNG image data.... Strangely, this code works when using Postman, but I am concerned that it segfaults instead of throwing an exception when it doesn’t. Is there any way for me to both fix the Swift code that may be causing the issue as well as prevent future segfaults from happening due to malformed data?

Source: Windows Questions C++

LEAVE A COMMENT